A card metrics widget processes user-supplied configuration values using three different JavaScript mechanisms: eval(), new Function(), and parseFloat(). The same payload behaves differently in each. Identify which execution contexts are exploitable and which are safe.
Script injection occurs when user input is passed to a JavaScript execution API such as eval(), new Function(), or setTimeout(string). Unlike DOM XSS - which injects through the HTML parser - script injection goes directly into the JavaScript engine. The payload does not need tags or event handlers; plain JavaScript statements are enough. The fix is to never pass user input to these APIs and to use safe alternatives like parseFloat() or JSON.parse() for data that only needs to be a value.
| True Negative | A valid numeric expression like 50 + 25 in the eval field computes correctly and returns a number. |
| Bug Found | alert(1) passed to eval() executes JavaScript in the page origin. No tags needed. |
| Bug Found | alert(1) passed to new Function() also executes. The constructor compiles and runs any string as a function body. |
| True Positive | alert(1) passed to parseFloat() returns NaN - no execution, no eval context, safe by design. |
The widget computes a display price by calling eval(input) so users can enter expressions like 50 + 25. eval() cannot distinguish a math expression from an attack payload.
The widget builds a custom formatter using new Function('value', 'return ' + input). Any statement appended after a semicolon runs as part of the function body.
The visitor threshold field uses parseFloat(input). This is a pure data conversion with no execution context - it can only produce a number or NaN.
50 + 25 and click Evaluate - eval() computes the result correctly; this is the true negative that makes the bug non-obvious (true negative)alert(1) - eval() treats it as a function call and executes it immediately; no HTML tags required (bug found)value * 1.2 - the formatter returns 120 as expected; the API appears useful (true negative)1; alert(document.domain) - the semicolon ends the return expression and the injected statement runs as part of the compiled function body (bug found)alert(1) or '; DROP TABLE-- - parseFloat reads numeric characters from the start of the string and stops; nothing is executed (true positive)alert(1) payload - eval() and new Function() are both dangerous regardless of how the input arrives