Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Not by itself. PhantomJS’s page.evaluate() runs an application-supplied function in the page’s JavaScript context. The injection risk arises when your application puts attacker-controlled text into that function and evaluates it as source code—for example, by calling eval(userInput). In that design, the caller controls JavaScript executed in the page context. That does not, on its own, prove the code can escape into the server process.
When does page.evaluate() become an injection vulnerability?
The important distinction is between the evaluation API and the input passed to it. A call to page.evaluate() is not automatically a JavaScript injection flaw. The dangerous pattern is accepting an untrusted string as JavaScript source and evaluating it inside the page callback.
For example, if an endpoint accepts a condition from a user and passes it to a callback like this, the user controls the code that runs in the page context:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11var result = page.evaluate(function (condition) {
return eval(condition);
}, userSuppliedCondition);
The issue is the combination of untrusted input and eval(), not the fact that the code happens to run through page.evaluate(). MDN’s documentation for eval() warns that evaluating untrusted strings can run code with the caller’s privileges. Avoid treating a user-specified expression as a harmless readiness condition.
#1 Best Overall
By contrast, an application-authored callback that receives constrained data is a different design:
var exists = page.evaluate(function (selector) {
return document.querySelector(selector) !== null;
}, validatedSelector);
Here the callback is fixed in application code. The selector is data interpreted by the DOM API, not JavaScript source compiled by eval(). Validate and constrain selectors to what your feature needs; changing the input type reduces the code-injection risk, but it does not remove the need to assess what page content or DOM information the caller may access.
Does page-context code execute commands on the server?
Arbitrary JavaScript passed to eval() inside page.evaluate() means caller-controlled code executes in the page context. Whether that can escape the page context and execute commands on the host is a separate security question. The available PhantomJS documentation and vulnerability records do not establish a reliable sandbox guarantee—or a demonstrated host-process escape—for every PhantomJS build and deployment. Do not claim either that escape is inevitable or that the page context is a proven security boundary for hostile code.
Recommended Free Tools
For threat modeling, distinguish these boundaries explicitly:
Rank #2
- Input to page code: Does a user control source code, a selector, a named condition, or ordinary data?
- Page context: What can the evaluated code do with the loaded page and its JavaScript environment?
- Host integration: What PhantomJS features, callbacks, files, processes, or other capabilities are available to the page or bridge in the deployed build?
- Loaded URL: What happens when the requested site itself is hostile or can reach internal resources?
These are related attack surfaces, but evidence of a flaw in one is not proof of a flaw in all the others. In particular, source-code injection into the page is already a security problem even if no host escape has been shown.
How to replace arbitrary conditions safely
Keep executable code under application control. Decide what the caller needs to request, then expose that request as structured data with a narrow, documented meaning.
Use fixed named checks
If callers need a readiness check, expose a finite list of application-authored checks rather than expressions. Validate the supplied name in the host process, then execute the fixed callback:
var checks = {
titlePresent: function () {
return document.title.length > 0;
},
mainVisible: function () {
var main = document.querySelector('main');
return !!main && main.getBoundingClientRect().width > 0;
}
};
// Validate checkName against this allow-list before calling page.evaluate.
var ready = page.evaluate(function (name) {
var checks = {
titlePresent: function () {
return document.title.length > 0;
},
mainVisible: function () {
var main = document.querySelector('main');
return !!main && main.getBoundingClientRect().width > 0;
}
};
return Object.prototype.hasOwnProperty.call(checks, name)
? checks[name]()
: false;
}, checkName);
The function definitions shown inside the callback are authored by the application; name selects among them rather than becoming code. Keep the host-side validation too, so unsupported names are rejected clearly instead of silently treated as a successful request.
Pass JSON-shaped data, not executable strings
For configuration or values, define a schema and pass parsed data. Use JSON.parse() for serialized JSON rather than eval(). JSON carries data; it does not preserve JavaScript functions or arbitrary expressions. Reject unexpected keys, types, lengths, and values before passing data into page code.
Constrain selectors and rules
If users need to choose a DOM target, accept a selector as data, impose length and syntax limits, and consider an allow-list of supported selectors or named targets. For more complex readiness logic, define a small rule format—such as a named selector plus a supported visibility condition—and interpret only those documented operations in application code. Do not turn a rule string into JavaScript to save implementation effort.
Keep URL loading as a separate control
Validating the code input does not make arbitrary page loading safe. If callers can choose URLs, handle destination access and redirects as a separate boundary, especially when the renderer runs in an environment that can reach internal services or local files. PhantomJS’s security history includes a distinct page.open() file-read issue; that issue is not proof that page.evaluate() itself is defective, but it is a reason not to collapse page loading and code evaluation into one risk assessment.
What PhantomJS vulnerability records do—and do not—show
MITRE’s CVE-2019-17221 record describes arbitrary file reading through page.open() in PhantomJS through version 2.1.1 when attacker-supplied HTML is loaded, and notes that PhantomJS is no longer developed. This is a separate issue from an application calling eval() on user-provided text inside page.evaluate().
Rank #4
NVD’s CVE-2016-10661 concerns the phantomjs-cheniu package downloading binary resources over HTTP, with possible interception and substitution. It is package-specific and should not be generalized into a claim that upstream PhantomJS’s page.evaluate() API has that flaw.
These records matter when assessing a legacy PhantomJS deployment, but neither changes the direct answer: page.evaluate() is an evaluation API, while evaluating caller-controlled source with eval() creates the injection condition.
Can CSP or Trusted Types make arbitrary user code safe?
No. Content Security Policy and Trusted Types can be useful defense-in-depth controls in runtimes that support them, but they are not a justification for exposing arbitrary user scripts. The W3C Trusted Types specification describes an injection sink as a powerful API that should receive trusted, validated, or appropriately sanitized input. It also cautions that an attacker-supplied string passed to eval() is a security vulnerability, while distinguishing safe from unsafe uses can be difficult.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
A Trusted Types label is not proof that a value is safe; the application’s policy and validation still matter. Support for Trusted Types in a particular legacy PhantomJS WebKit build is not established here. Verify controls against the exact deployed runtime before relying on them, and prefer removing the dynamic evaluation path altogether.
Best Value
Implementation checklist
- Search for
eval(),new Function(), and other dynamic compilation paths inside page callbacks and their helpers. - Trace each value from the request boundary to the evaluation call. Treat user input, stored user content, and content from untrusted pages as untrusted unless validated for a specific data schema.
- Replace free-form JavaScript with application-authored callbacks, named checks, constrained selectors, or structured rules.
- Reject unsupported names and malformed values before calling
page.evaluate(); do not silently fall back to executing the original string. - Review page URL fetching separately from callback input, including what network and file resources the renderer can access.
- Assess the exact PhantomJS version and host integration. Do not infer a secure sandbox from the fact that code runs in a page context.
- Where feasible, plan migration away from an unmaintained runtime; do not treat migration alone as a fix for application code that still evaluates untrusted source.
Troubleshooting common designs
“The user only supplies a condition to wait for.”
If the condition is a JavaScript string executed by eval(), the user supplies code, regardless of the feature label. Replace it with a finite set of checks or a constrained rule format.
“The callback is inside page.evaluate(), so it is sandboxed.”
The callback runs in the page context, but the reviewed material does not establish a complete security boundary between that context and every PhantomJS build or host integration. Do not use that assumption to justify hostile scripts.
“We removed eval(), but users still choose a selector.”
A selector passed to querySelector() is not automatically JavaScript injection. Keep it as data, validate its shape and scope, and consider an allow-list if callers should only inspect specific elements. Reassess separately if any later code turns the selector into source code.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errors“The page is trusted, so the input is safe.”
Trust in the destination does not make a user-supplied condition safe, and a trusted input does not make a hostile destination safe. Track code input and loaded content as distinct sources.
Or skip the browser setup
If your task is to capture a website image or PDF rather than run a custom condition in PhantomJS, ScreenshotNeo is a screenshot API and MCP server; it is not a replacement for evaluating arbitrary JavaScript or a mitigation for an unsafe eval() design. A one-request capture looks like this; see the API documentation for parameters:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Before capture, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.
Frequently Asked Questions
Would URL validation alone fix the injection risk?
No. URL validation addresses which page is loaded; it does not make a condition string safe if that string is still passed to eval(). Treat destination control and executable-input control as separate checks.
Can JSON represent the same arbitrary JavaScript conditions?
No. JSON represents data, not executable functions or expressions. If callers need configurable behavior, define and validate a limited rule schema, then interpret only its supported operations in application-authored code.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

