Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
chrome-remote-interface
Advanced tools
The chrome-remote-interface npm package is a tool that allows you to interact with the Chrome DevTools Protocol. This enables you to control, inspect, and debug web pages and web applications programmatically.
Remote Debugging
This feature allows you to remotely debug a web page. The code sample demonstrates how to navigate to a URL, wait for the page to load, capture a screenshot, and save it to a file.
const CDP = require('chrome-remote-interface');
CDP(async (client) => {
const {Network, Page} = client;
await Network.enable();
await Page.enable();
Page.navigate({url: 'https://example.com'});
Page.loadEventFired(async () => {
const result = await Page.captureScreenshot();
require('fs').writeFileSync('screenshot.png', result.data, 'base64');
client.close();
});
}).on('error', (err) => {
console.error(err);
});
Network Monitoring
This feature allows you to monitor network requests and responses. The code sample demonstrates how to log URLs of requests and responses.
const CDP = require('chrome-remote-interface');
CDP(async (client) => {
const {Network} = client;
await Network.enable();
Network.requestWillBeSent((params) => {
console.log('Request:', params.request.url);
});
Network.responseReceived((params) => {
console.log('Response:', params.response.url);
});
}).on('error', (err) => {
console.error(err);
});
JavaScript Execution
This feature allows you to execute JavaScript in the context of the web page. The code sample demonstrates how to evaluate a JavaScript expression to get the document title.
const CDP = require('chrome-remote-interface');
CDP(async (client) => {
const {Runtime} = client;
await Runtime.enable();
const result = await Runtime.evaluate({expression: 'document.title'});
console.log('Title:', result.result.value);
client.close();
}).on('error', (err) => {
console.error(err);
});
Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. It is more user-friendly and higher-level compared to chrome-remote-interface, making it easier to perform common tasks like taking screenshots, generating PDFs, and automating form submissions.
Selenium WebDriver is a popular tool for automating web applications for testing purposes. It provides a more comprehensive solution for browser automation, supporting multiple browsers and programming languages. Compared to chrome-remote-interface, it is more focused on testing and less on low-level browser control.
Playwright is a Node library to automate Chromium, Firefox, and WebKit with a single API. It is similar to Puppeteer but supports multiple browsers. It offers more advanced features like capturing videos of test runs and intercepting network requests, making it a more versatile tool compared to chrome-remote-interface.
Chrome Debugging Protocol interface that helps to instrument Chrome (or any other suitable implementation) by providing a simple abstraction of commands and notifications using a straightforward JavaScript API.
This module is one of the many third-party protocol clients.
The following snippet loads https://github.com
and dumps every request made:
const CDP = require('chrome-remote-interface');
CDP(function (client) {
with (client) {
Network.requestWillBeSent(function (params) {
console.log(params.request.url);
});
Page.loadEventFired(function () {
close();
});
Network.enable();
Page.enable();
once('ready', function () {
Page.navigate({'url': 'https://github.com'});
});
}
}).on('error', function (err) {
console.error('Cannot connect to remote endpoint:', err);
});
npm install chrome-remote-interface
Install globally (-g
) to just use the bundled client.
This module should work with every application implementing the Chrome Debugging Protocol. In particular, it has been tested against the following implementations:
Implementation | Protocol version | Protocol | List | New | Activate | Close | Version |
---|---|---|---|---|---|---|---|
Google Chrome | tip-of-tree | yes | yes | yes | yes | yes | yes |
Microsoft Edge | partial | yes | yes | no | no | no | yes |
Node.js (v6.3.0+) | node | yes | no | no | no | no | yes |
Safari (iOS) | partial | no | yes | no | no | no | no |
An instance of either Chrome itself or another implementation needs to be
running on a known port in order to use this module (defaults to
localhost:9222
).
Start Chrome with the --remote-debugging-port
option, for example:
google-chrome --remote-debugging-port=9222
Plug the device and enable the port forwarding, for example:
adb forward tcp:9222 localabstract:chrome_devtools_remote
In order to be inspectable, a WebView must be configured for debugging and the corresponding process ID must be known. There are several ways to obtain it, for example:
adb shell grep -a webview_devtools_remote /proc/net/unix
Finally, port forwarding can be enabled as follows:
adb forward tcp:9222 localabstract:webview_devtools_remote_<pid>
Install and run the Edge Diagnostics Adapter.
Start Node.js with the --inspect
option, for example:
node --inspect=9222 script.js
Install and run the iOS WebKit Debug Proxy.
This module comes with a bundled client application that can be used to interactively control a remote instance.
The bundled client exposes subcommands to interact with the HTTP frontend
(e.g., List, New, etc.),
run with --help
to display the list of available options.
Here are some examples:
$ chrome-remote-interface new 'http://example.com'
{ description: '',
devtoolsFrontendUrl: '/devtools/inspector.html?ws=localhost:9222/devtools/page/b049bb56-de7d-424c-a331-6ae44cf7ae01',
id: 'b049bb56-de7d-424c-a331-6ae44cf7ae01',
thumbnailUrl: '/thumb/b049bb56-de7d-424c-a331-6ae44cf7ae01',
title: '',
type: 'page',
url: 'http://example.com/',
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/b049bb56-de7d-424c-a331-6ae44cf7ae01' }
$ chrome-remote-interface close 'b049bb56-de7d-424c-a331-6ae44cf7ae01'
Using the inspect
subcommand it is possible to
perform command execution
and event binding in a REPL fashion. But unlike
the regular API the callbacks are overridden to conveniently display the result
of the commands and the message of the events. Also, the event binding is
simplified here, executing a shorthand method (e.g., Page.loadEventFired()
)
toggles the event registration.
Remember that the REPL interface provides completion.
Here is a sample session:
$ chrome-remote-interface inspect
>>> Runtime.evaluate({expression: 'window.location.toString()'})
{ result:
{ result:
{ type: 'string',
value: 'https://www.google.it/_/chrome/newtab?espv=2&ie=UTF-8' },
wasThrown: false } }
>>> Page.enable()
{ result: {} }
>>> Page.loadEventFired() // registered
{ 'Page.loadEventFired': true }
>>> Page.loadEventFired() // unregistered
{ 'Page.loadEventFired': false }
>>> Page.loadEventFired() // registered
{ 'Page.loadEventFired': true }
>>> Page.navigate({url: 'https://github.com'})
{ result: { frameId: '28677.1' } }
{ 'Page.loadEventFired': { timestamp: 21385.383076 } }
>>> Runtime.evaluate({expression: 'window.location.toString()'})
{ result:
{ result: { type: 'string', value: 'https://github.com/' },
wasThrown: false } }
To reduce the amount of data displayed by the event listeners it is possible to provide a filter function. In this example only the resource URL is shown:
$ chrome-remote-interface inspect
>>> Network.enable()
{ result: {} }
>>> Network.requestWillBeSent(params => params.request.url)
{ 'Network.requestWillBeSent': 'params => params.request.url' }
>>> Page.navigate({url: 'https://www.wikipedia.org'})
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/' }
{ result: { frameId: '5530.1' } }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia_wordmark.png' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia-logo-v2.png' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/js/index-3b68787aa6.js' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/js/gt-ie9-c84bf66d33.js' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/sprite-bookshelf_icons.png?16ed124e8ca7c5ce9d463e8f99b2064427366360' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/sprite-project-logos.png?9afc01c5efe0a8fb6512c776955e2ad3eb48fbca' }
In both the REPL and the regular API every object of the protocol is decorated
with the meta information found within the descriptor. In addition The
category
field is added, which determines if the member is a command
, an
event
or a type
.
For example to learn how to call Page.navigate
:
>>> Page.navigate
{ [Function]
category: 'command',
parameters: { url: { type: 'string', description: 'URL to navigate the page to.' } },
returns:
[ { name: 'frameId',
'$ref': 'FrameId',
hidden: true,
description: 'Frame id that will be navigated.' } ],
description: 'Navigates current page to the given URL.',
handlers: [ 'browser', 'renderer' ] }
To learn about the parameters returned by the Network.requestWillBeSent
event:
>>> Network.requestWillBeSent
{ [Function]
category: 'event',
description: 'Fired when page is about to send HTTP request.',
parameters:
{ requestId: { '$ref': 'RequestId', description: 'Request identifier.' },
frameId:
{ '$ref': 'Page.FrameId',
description: 'Frame identifier.',
hidden: true },
loaderId: { '$ref': 'LoaderId', description: 'Loader identifier.' },
documentURL:
{ type: 'string',
description: 'URL of the document this request is loaded for.' },
request: { '$ref': 'Request', description: 'Request data.' },
timestamp: { '$ref': 'Timestamp', description: 'Timestamp.' },
wallTime:
{ '$ref': 'Timestamp',
hidden: true,
description: 'UTC Timestamp.' },
initiator: { '$ref': 'Initiator', description: 'Request initiator.' },
redirectResponse:
{ optional: true,
'$ref': 'Response',
description: 'Redirect response data.' },
type:
{ '$ref': 'Page.ResourceType',
optional: true,
hidden: true,
description: 'Type of this resource.' } } }
To inspect the Network.Request
(note that unlike commands and events, types
are named in upper camel case) type:
>>> Network.Request
{ category: 'type',
id: 'Request',
type: 'object',
description: 'HTTP request data.',
properties:
{ url: { type: 'string', description: 'Request URL.' },
method: { type: 'string', description: 'HTTP request method.' },
headers: { '$ref': 'Headers', description: 'HTTP request headers.' },
postData:
{ type: 'string',
optional: true,
description: 'HTTP POST request data.' },
mixedContentType:
{ optional: true,
type: 'string',
enum: [Object],
description: 'The mixed content status of the request, as defined in http://www.w3.org/TR/mixed-content/' },
initialPriority:
{ '$ref': 'ResourcePriority',
description: 'Priority of the resource request at the time request is sent.' } } }
chrome-remote-interface
uses the local version of the protocol descriptor by
default. This file is manually updated from time to time using
scripts/update-protocol.sh
and pushed to this repository.
This behavior can be changed by setting the remote
option to true
upon connection, in which case the remote instance is
asked to provide its own protocol descriptor.
Currently Chrome is not able to do that (see #10), so the protocol descriptor is fetched from the proper source repository.
To override the above behavior there are basically three options:
pass a custom protocol descriptor upon connection
(protocol
option);
update the local copy with scripts/update-protocol.sh
(not present when
fetched with npm install
).
This module is able to run within a web context, with obvious limitations
though, namely external HTTP requests
(List, New, etc.) cannot
be performed directly, for this reason the user must provide a global
criRequest
in order to use them:
function criRequest(options, callback) {}
options
is the same object used by the Node.js http
module and callback
is
a function taking two arguments: err
(JavaScript Error
object or null
) and
data
(string result).
It just works, simply require this module:
const CDP = require('chrome-remote-interface');
To use a non-minified version manually run webpack with:
DEBUG=true npm run webpack
To generate a JavaScript file that can be used with a <script>
element:
run npm install
from the root directory;
manually run webpack with:
TARGET=var npm run webpack
TARGET=var DEBUG=true npm run webpack
use as:
<script>
function criRequest(options, callback) { /*...*/ }
</script>
<script src="chrome-remote-interface.js"></script>
The API consists of three parts:
DevTools methods (for those implementations that support them, e.g., List, New, etc.);
connection establishment;
the actual protocol interaction.
Connects to a remote instance using the Chrome Debugging Protocol.
options
is an object with the following optional properties:
host
: HTTP frontend host. Defaults to localhost
;
port
: HTTP frontend port. Defaults to 9222
;
tab
: determines which tab this client should attach to. The behavior changes
according to the type:
function
that takes the array returned by the List
method and returns
the numeric index of a tab;object
like those returned by the New
and List
methods;string
representing the raw WebSocket URL, in this case host
and
port
are not used to fetch the tab list.Defaults to a function which returns the currently active tab (function (tabs) { return 0; }
);
protocol
: Chrome Debugging Protocol descriptor object. Defaults to use the
protocol chosen according to the remote
option;
remote
: a boolean indicating whether the protocol must be fetched remotely
or if the local version must be used. It has no effect if the protocol
option is set. Defaults to false
.
These options are also valid properties of all the instances of the CDP
class.
callback
is a listener automatically added to the connect
event of the
returned EventEmitter
. When callback
is omitted a Promise
object is
returned which becomes fulfilled if the connect
event is triggered and
rejected if any of the disconnect
or error
events are triggered.
The EventEmitter
supports the following events:
function (client) {}
Emitted when the connection to the WebSocket is established.
client
is an instance of the CDP
class.
function () {}
Emitted when an instance closes the WebSocket connection.
This may happen for example when the user opens DevTools for the currently inspected Chrome tab.
function (err) {}
Emitted when http://host:port/json
cannot be reached or if it is not possible
to connect to the WebSocket.
err
is an instance of Error
.
Fetch the Chrome Debugging Protocol descriptor.
options
is an object with the following optional properties:
host
: HTTP frontend host. Defaults to localhost
;port
: HTTP frontend port. Defaults to 9222
;remote
: a boolean indicating whether the protocol must be fetched remotely
or if the local version must be returned. If it is not possible to fulfill the
request then the local version is used. Defaults to false
.callback
is executed when the protocol is fetched, it gets the following
arguments:
err
: a Error
object indicating the success status;protocol
: an object with the following properties:
remote
: a boolean indicating whether the returned descriptor is the
remote version or not (due to user choice or error);descriptor
: the Chrome Debugging Protocol descriptor.When callback
is omitted a Promise
object is returned.
For example:
const CDP = require('chrome-remote-interface');
CDP.Protocol(function (err, protocol) {
if (!err) {
console.log(JSON.stringify(protocol.descriptor, null, 4));
}
});
Request the list of the available open tabs of the remote instance.
options
is an object with the following optional properties:
host
: HTTP frontend host. Defaults to localhost
;port
: HTTP frontend port. Defaults to 9222
.callback
is executed when the list is correctly received, it gets the
following arguments:
err
: a Error
object indicating the success status;tabs
: the array returned by http://host:port/json/list
containing the tab
list.When callback
is omitted a Promise
object is returned.
For example:
const CDP = require('chrome-remote-interface');
CDP.List(function (err, tabs) {
if (!err) {
console.log(tabs);
}
});
Create a new tab in the remote instance.
options
is an object with the following optional properties:
host
: HTTP frontend host. Defaults to localhost
;port
: HTTP frontend port. Defaults to 9222
;url
: URL to load in the new tab. Defaults to about:blank
.callback
is executed when the tab is created, it gets the following arguments:
err
: a Error
object indicating the success status;tab
: the object returned by http://host:port/json/new
containing the tab.When callback
is omitted a Promise
object is returned.
For example:
const CDP = require('chrome-remote-interface');
CDP.New(function (err, tab) {
if (!err) {
console.log(tab);
}
});
Activate an open tab of the remote instance.
options
is an object with the following properties:
host
: HTTP frontend host. Defaults to localhost
;port
: HTTP frontend port. Defaults to 9222
;id
: Tab id. Required, no default.callback
is executed when the response to the activation request is
received. It gets the following arguments:
err
: a Error
object indicating the success status;When callback
is omitted a Promise
object is returned.
For example:
const CDP = require('chrome-remote-interface');
CDP.Activate({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {
if (!err) {
console.log('success! tab is activated');
}
});
Close an open tab of the remote instance.
options
is an object with the following properties:
host
: HTTP frontend host. Defaults to localhost
;port
: HTTP frontend port. Defaults to 9222
;id
: Tab id. Required, no default.callback
is executed when the response to the close request is received. It
gets the following arguments:
err
: a Error
object indicating the success status;When callback
is omitted a Promise
object is returned.
For example:
const CDP = require('chrome-remote-interface');
CDP.Close({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {
if (!err) {
console.log('success! tab is closing');
}
});
Note that the callback is fired when the tab is queued for removal, but the actual removal will occur asynchronously.
Request version information from the remote instance.
options
is an object with the following optional properties:
host
: HTTP frontend host. Defaults to localhost
;port
: HTTP frontend port. Defaults to 9222
.callback
is executed when the version information is correctly received, it
gets the following arguments:
err
: a Error
object indicating the success status;info
: a JSON object returned by http://host:port/json/version
containing
the version information.When callback
is omitted a Promise
object is returned.
For example:
const CDP = require('chrome-remote-interface');
CDP.Version(function (err, info) {
if (!err) {
console.log(info);
}
});
function (message) {}
Emitted when the remote instance sends any notification through the WebSocket.
message
is the object received, it has the following properties:
method
: a string describing the notification (e.g.,
'Network.requestWillBeSent'
);params
: an object containing the payload.Refer to the Chrome Debugging Protocol specification for more information.
For example:
client.on('event', function (message) {
if (message.method === 'Network.requestWillBeSent') {
console.log(message.params);
}
});
<method>
'function (params) {}
Emitted when the remote instance sends a notification for <method>
through the
WebSocket.
params
is an object containing the payload.
This is just a utility event which allows to easily listen for specific
notifications (see 'event'
), for example:
client.on('Network.requestWillBeSent', console.log);
function () {}
Emitted every time that there are no more pending commands waiting for a
response from the remote instance. The interaction is asynchronous so the only
way to serialize a sequence of commands is to use the callback provided by
the send
method. This event acts as a
barrier and it is useful to avoid the callback hell in certain simple
situations.
For example to load a URL only after having enabled the notifications of both
Network
and Page
domains:
client.Network.enable();
client.Page.enable();
client.once('ready', function () {
client.Page.navigate({'url': 'https://github.com'});
});
In this particular case, not enforcing this kind of serialization may cause that the remote instance does not properly deliver the desired notifications the client.
Issue a command to the remote instance.
method
is a string describing the command.
params
is an object containing the payload.
callback
is executed when the remote instance sends a response to this
command, it gets the following arguments:
error
: a boolean value indicating the success status, as reported by the
remote instance;response
: an object containing either the response (result
field, if
error === false
) or the indication of the error (error
field, if error === true
).When callback
is omitted a Promise
object is returned instead, with the
fulfilled/rejected states implemented according to the error
parameter.
Note that the field id
mentioned in the Chrome Debugging Protocol
specification is managed internally and it is not exposed to the user.
For example:
client.send('Page.navigate', {'url': 'https://github.com'}, console.log);
<domain>
.<method>
([params], [callback])Just a shorthand for:
client.send('<domain>.<method>', params, callback);
For example:
client.Page.navigate({'url': 'https://github.com'}, console.log);
<domain>
.<event>
(callback)Just a shorthand for:
client.on('<domain>.<event>', callback);
For example:
client.Network.requestWillBeSent(console.log);
Close the connection to the remote instance.
callback
is executed when the WebSocket is successfully closed.
When callback
is omitted a Promise
object is returned.
FAQs
Chrome Debugging Protocol interface
The npm package chrome-remote-interface receives a total of 0 weekly downloads. As such, chrome-remote-interface popularity was classified as not popular.
We found that chrome-remote-interface demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.