Copyright © 2024 the Contributors to the Web Serial API Specification, published by the Web Platform Incubator Community Group under the W3C Community Contributor License Agreement (CLA). A human-readable summary is available.
This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.
This is a work in progress. All contributions welcome.GitHub Issues are preferred for discussion of this specification.
Serial
interface
WebIDL[Exposed=(DedicatedWorker, Window), SecureContext]
interface Serial
: EventTarget {
attribute EventHandler onconnect
;
attribute EventHandler ondisconnect
;
Promise<sequence<SerialPort
>> getPorts
();
[Exposed=Window] Promise<SerialPort
> requestPort
(optional SerialPortRequestOptions
options = {});
};
The requestPort
()
method steps are:
serial
", reject
promise with a "SecurityError
" DOMException
and return
promise.
SecurityError
" DOMException
and return promise.
filters
"] is present,
then for each filter in
options["filters
"] run the following
steps:
bluetoothServiceClassId
"] is
present:
usbVendorId
"] is present,
reject promise with a TypeError
and return promise.
usbProductId
"] is present,
reject promise with a TypeError
and return promise.
usbVendorId
"] is not
present, reject promise with a TypeError
and return
promise.
SerialPortFilter
cannot be empty and if
usbProductId
is specified then
usbVendorId
must also be specified.
BluetoothServiceUUID
uuid supported by the device:
allowedBluetoothServiceClassIds
"]
is present and contains uuid:
SerialPort
representing the service on the Bluetooth device.
SerialPort
representing
the port.
filters
"]
if present and allPorts otherwise.
NotFoundError
" DOMException
and abort these steps.
SerialPort
representing the
port chosen by the user.
A serial port is available if it is a wired serial port and the port is physically connected to the system, or if it is a wireless serial port and the wireless device hosting the port is registered with the system.
WebIDLdictionary SerialPortRequestOptions
{
sequence<SerialPortFilter
> filters
;
sequence<BluetoothServiceUUID> allowedBluetoothServiceClassIds
;
};
filters
member
allowedBluetoothServiceClassIds
member
BluetoothServiceUUID
values representing Bluetooth
service class IDs. Bluetooth ports with custom service class IDs
are excluded from the list of ports presented to the user unless
the service class ID is included in this list.
WebIDLdictionary SerialPortFilter
{
unsigned short usbVendorId
;
unsigned short usbProductId
;
BluetoothServiceUUID bluetoothServiceClassId
;
};
usbVendorId
member
usbProductId
member
bluetoothServiceClassId
member
A serial port port matches the filter
filter if these steps return true
:
getInfo
()
.
bluetoothServiceClassId
"] is
present:
false
.
bluetoothServiceClassId
"]
is equal to
info["bluetoothServiceClassId
"], return
true
.
false
.
usbVendorId
"] is not present,
return true
.
false
.
usbVendorId
"] is not equal to
filter["usbVendorId
"], return false
.
usbProductId
"] is not
present, return true
.
usbProductId
"] is not equal to
filter["usbProductId
"], return false
.
true
.
A serial port port matches any filter in a sequence of
SerialPortFilter
if these steps return true
:
true
.
false
.
The getPorts
()
method steps are:
"serial"
, reject promise with a
"SecurityError
" DOMException
and return promise.
requestPort
()
.
SerialPort
s
representing the ports in availablePorts.
onconnect
is an event handler IDL attribute for the
connect event type.
ondisconnect
is an event handler IDL attribute for
the disconnect event type.
WebIDL[Exposed=(DedicatedWorker,Window), SecureContext]
interface SerialPort
: EventTarget {
attribute EventHandler onconnect
;
attribute EventHandler ondisconnect
;
readonly attribute boolean connected
;
readonly attribute ReadableStream readable
;
readonly attribute WritableStream writable
;
SerialPortInfo
getInfo
();
Promise<undefined> open
(SerialOptions
options);
Promise<undefined> setSignals
(optional SerialOutputSignals
signals = {});
Promise<SerialInputSignals
> getSignals
();
Promise<undefined> close
();
Promise<undefined> forget
();
};
Methods on this interface typically complete asynchronously, queuing work on the serial port task source.
The get the parent algorithm for SerialPort
returns the same
Serial
instance that is returned by the SerialPort
's relevant global object's Navigator
object's serial
getter.
Instances of SerialPort
are created with the internal slots
described in the following table:
Internal slot | Initial value | Description (non-normative) |
---|---|---|
[[state]] |
"closed"
|
Tracks the active state of the SerialPort
|
[[bufferSize]] | undefined | The amount of data to buffer for transmit and receive |
[[connected]] |
false
|
A flag indicating the logical connection state of serial port |
[[readable]] |
null
|
A ReadableStream that receives data from the port
|
[[readFatal]] |
false
|
A flag indicating that the port has encountered a fatal read error |
[[writable]] |
null
|
A WritableStream that transmits data to the port
|
[[writeFatal]] |
false
|
A flag indicating that the port has encountered a fatal write error |
[[pendingClosePromise]] |
null
|
A Promise used to wait for readable and
writable to close
|
onconnect
is an event handler IDL attribute for
the connect
event type.
When a serial port that the user has allowed the site to access as the
result of a previous call to requestPort
()
becomes
logically connected, run the following steps:
SerialPort
representing the port.
[[connected]]
to true
.
connect
at port with its
bubbles
attribute initialized to true
.
A serial port is logically connected if it is a wired serial port and the port is physically connected to the system, or if it is a wireless serial port and the system has active connections to the wireless device (e.g. an open Bluetooth L2CAP channel).
ondisconnect
is an event handler IDL attribute
for the disconnect
event type.
When a serial port that the user has allowed the site to access as the
result of a previous call to requestPort
()
is no longer
logically connected, run the following steps:
SerialPort
representing the port.
[[connected]]
to false
.
disconnect
at port with its
bubbles
attribute initialized to true
.
getInfo
()
method steps are:
SerialPortInfo
dictionary.
usbVendorId
"] to the vendor ID
of the device.
usbProductId
"] to the product
ID of the device.
bluetoothServiceClassId
"] to
the service class UUID of the Bluetooth service.
WebIDLdictionary SerialPortInfo
{
unsigned short usbVendorId
;
unsigned short usbProductId
;
BluetoothServiceUUID bluetoothServiceClassId
;
};
usbVendorId
member
undefined
.
usbProductId
member
undefined
.
bluetoothServiceClassId
member
BluetoothServiceUUID
containing the service class UUID.
Otherwise it will be undefined
.
The open
()
method steps are:
[[state]]
is not "closed"
, reject
promise with an "InvalidStateError
" DOMException
and return
promise.
dataBits
"] is not 7 or 8, reject
promise with TypeError
and return promise.
stopBits
"] is not 1 or 2, reject
promise with TypeError
and return promise.
bufferSize
"] is 0, reject
promise with TypeError
and return promise.
bufferSize
"] is
larger than the implementation is able to support, reject promise
with a TypeError
and return promise.
[[state]]
to "opening"
.
NetworkError
"
DOMException
and abort these steps.
[[state]]
to "opened"
.
[[bufferSize]]
to
options["bufferSize
"].
undefined
.
WebIDLdictionary SerialOptions
{
[EnforceRange] required unsigned long baudRate
;
[EnforceRange] octet dataBits
= 8;
[EnforceRange] octet stopBits
= 1;
ParityType
parity
= "none";
[EnforceRange] unsigned long bufferSize
= 255;
FlowControlType
flowControl
= "none";
};
baudRate
member
baudRate
is the only required member of this
dictionary. While there are common default for other connection
parameters it is important for developers to consider and
consult with the documentation for devices they intend to
connect to determine the correct values. While some values are
common there is no standard baud rate. Requiring this parameter
reduces the potential for confusion if an arbitrary default
were chosen by this specification.
dataBits
member
stopBits
member
parity
member
bufferSize
member
flowControl
member
WebIDLenum ParityType
{
"none
",
"even
",
"odd
"
};
none
even
odd
WebIDLenum FlowControlType
{
"none
",
"hardware
"
};
none
hardware
The connected
getter steps are:
[[connected]]
.
The readable
getter steps are:
[[readable]]
is not null
, return
this.[[readable]]
.
[[state]]
is not "opened"
, return
null
.
[[readFatal]]
is true
, return
null
.
ReadableStream
.
[[readable]]
.
[[readable]]
's
current BYOB request view is non-null, then
set desiredSize to this.[[readable]]
's
current BYOB request view's
byte length.
[[readable]]
's
current BYOB request view is
non-null, then write bytes into
this.[[readable]]
's
current BYOB request view, and set
view to this.[[readable]]
's
current BYOB request view.
Uint8Array
from bytes in this's relevant Realm.
[[readable]]
.
[[readable]]
with a
"BufferOverrunError
" DOMException
and
invoke the steps to handle closing the readable stream.
[[readable]]
with a
"BreakError
" DOMException
and invoke the
steps to handle closing the readable stream.
[[readable]]
with a
"FramingError
" DOMException
and invoke
the steps to handle closing the readable stream.
[[readable]]
with a
"ParityError
" DOMException
and invoke
the steps to handle closing the readable stream.
[[readable]]
with an
"UnknownError
" DOMException
and invoke the steps
to handle closing the readable stream.
[[readFatal]]
to
true
,
[[readable]]
with a
"NetworkError
" DOMException
.
undefined
.
undefined
.
[[bufferSize]]
.
[[readable]]
to stream.
[[readable]]
to null
.
[[writable]]
is null
and
this.[[pendingClosePromise]]
is not null
,
resolve this.[[pendingClosePromise]]
with
undefined
.
The writable
getter steps are:
[[writable]]
is not null
, return
this.[[writable]]
.
[[state]]
is not "opened"
, return
null
.
[[writeFatal]]
is true
, return
null
.
WritableStream
.
BufferSource
, reject promise with a TypeError
and
return promise. Otherwise, save the result of the conversion in
source.
undefined
.
Promise
returned by a
previous invocation of this algorithm has resolved.
For efficiency an implementation is allowed to
resolve this Promise
early in order to coalesce
multiple chunks waiting in the WritableStream
's
internal queue into a single request to the operating
system.
UnknownError
"
DOMException
.
[[writeFatal]]
to
true
.
NetworkError
"
DOMException
.
undefined
.
undefined
.
[[bufferSize]]
,
and
sizeAlgorithm set to a byte-counting size algorithm.
[[writable]]
to stream.
[[writable]]
to null
.
[[readable]]
is null
and
this.[[pendingClosePromise]]
is not null
,
resolve this.[[pendingClosePromise]]
with
undefined
.
The setSignals
()
method steps are:
[[state]]
is not "opened"
, reject
promise with an "InvalidStateError
" DOMException
and return
promise.
TypeError
and return promise.
dataTerminalReady
"] is
present, invoke the operating system to either assert (if true
)
or deassert (if false
) the "data terminal ready" or "DTR"
signal on the serial port.
requestToSend
"] is
present, invoke the operating system to either assert (if true
)
or deassert (if false
) the "request to send" or "RTS" signal on
the serial port.
break
"] is present,
invoke the operating system to either assert (if true
) or
deassert (if false
) the "break" signal on the serial port.
NetworkError
"
DOMException
.
undefined
.
WebIDLdictionary SerialOutputSignals
{
boolean dataTerminalReady
;
boolean requestToSend
;
boolean break
;
};
dataTerminalReady
requestToSend
break
getSignals
()
method steps are:
[[state]]
is not "opened"
, reject
promise with an "InvalidStateError
" DOMException
and return
promise.
NetworkError
"
DOMException
and abort these steps.
SerialInputSignals
.
dataCarrierDetect
"] to
true
if the "data carrier detect" or "DCD" signal has been
asserted by the device, and false
otherwise.
clearToSend
"] to true
if the "clear to send" or "CTS" signal has been asserted by the
device, and false
otherwise.
ringIndicator
"] to
true
if the "ring indicator" or "RI" signal has been asserted
by the device, and false
otherwise.
dataSetReady
"] to
true
if the "data set ready" or "DSR" signal has been asserted
by the device, and false
otherwise.
WebIDLdictionary SerialInputSignals
{
required boolean dataCarrierDetect
;
required boolean clearToSend
;
required boolean ringIndicator
;
required boolean dataSetReady
;
};
dataCarrierDetect
member
clearToSend
member
ringIndicator
member
dataSetReady
member
The close
()
method steps are:
[[state]]
is not "opened"
, reject
promise with an "InvalidStateError
" DOMException
and return
promise.
[[readable]]
or
a promise resolved with undefined
if
this.[[readable]]
is null
.
[[writable]]
or
a promise resolved with undefined
if
this.[[writable]]
is null
.
[[readable]]
and
this.[[writable]]
are null
, resolve
pendingClosePromise with undefined
.
[[pendingClosePromise]]
to
pendingClosePromise.
[[state]]
to "closing"
.
[[state]]
to "closed"
.
[[readFatal]]
and
this.[[writeFatal]]
to false
.
[[pendingClosePromise]]
to null
.
undefined
.
[[pendingClosePromise]]
to
null
.
The forget
()
method steps are:
undefined
.
[[state]]
to "forgetting"
.
requestPort
()
.
[[state]]
to "forgotten"
.
undefined
.
This specification relies on a blocklist file in the https://github.com/WICG/serial repository to restrict the set of ports a website can access.
The result of parsing the Bluetooth service class ID
blocklist at a URL url is a list of UUID
values
representing custom service IDs.
The Serial Port Profile service class ID is a
BluetoothServiceUUID
with value
"00001101-0000-1000-8000-00805f9b34fb
".
A {{BluetoothServiceUUID} serviceUuid is a
blocked Bluetooth service class UUID if the following steps
return true
:
BluetoothUUID
.getService
()
with serviceUuid.
true
.
false
.
-0000-1000-8000-00805f9b34fb
",
return true
.
false
.
This specification defines a feature that controls whether the
methods exposed by the serial
attribute on the
Navigator
object may be used.
The feature name for this feature is "serial
"`.
The default allowlist for this feature
is 'self'
.
This section is non-normative.
This API poses similar a security risk to [WEB-BLUETOOTH] and [WEBUSB] and so lessons from those are applicable here. The primary threats are:requestPort
()
pattern, which requires user interaction and
only supports granting access to a single device at a time. This prevents
drive-by attacks because a site cannot enumerate all connected devices to
determine whether a vulnerable device exists and must instead proactively
inform the user that it desires access. Implementations may also provide
a visual indication that a site is currently communicating with a device
and controls for revoking that permission at any time.
This specification requires the site to be served from a secure context in order to prevent malicious code from being injected by a network-based attacker. This ensures that the site identity shown to the user when making permission decisions is accurate. This specification also requires top-level documents to opt-in through [PERMISSIONS-POLICY] before allowing a cross-origin iframe to use the API. When combined with [CSP3] these mechanisms provide protection against malicious code injection attacks.
The remaining concern is the exploitation of a connected device through a phishing attack that convinces the user to grant a malicious site access to a device. These attacks can be used to either exploit the device’s capabilities as designed or to install malicious firmware on the device that will in turn attack the host computer. Host software may be vulnerable to attack because it improperly validates input from connected devices. Security research in this area has encouraged software vendors to treat connected devices as untrustworthy.
There is no mechanism that will completely prevent this type of attack because data sent from a page to the device is an opaque sequence of bytes. Efforts to block a particular type of data from being sent are likely be met by workarounds on the part of device manufacturers who nevertheless want to send this type of data to their devices.
User agents can implement additional mechanisms to control access to devices:
requestPort
()
unless added to an explicit allow list.
Systems administrators could apply such a setting across their managed fleet using enterprise policy controls. Such controls may allow the administrator to automatically grant selected sites access to particular devices and no others.
In addition, maintaining a list of vulnerable devices works well for USB and Bluetooth because those protocols define out-of-band mechanisms to gather device metadata. The make and model of such devices can thus be easily identified even if they present themselves to the host as a virtual serial ports. However, there are generic USB- or Bluetooth-to-serial adapters as well as systems with "real" serial ports using a DB-25, DE-9 or RJ-45 connector. For these there is no metadata that can be read to determine the identity of the device connected to the port and so blocking access to these is not possible.
This section is non-normative.
Serial ports and serial devices contain two kinds of sensitive information. When a port is a USB or Bluetooth device there are identifiers such as the vendor and product IDs (which identify the make and model) as well as a serial number or MAC address. The serial device itself may also have its own identifier that is available through commands sent via the serial port. The device may also store other private information which may or may not be identifying.In order to manage device permissions an implementation will likely store device identifiers such as the USB vendor ID, product ID and serial number in its user preferences file to be used as stable identifiers for devices the user has granted sites access to. These would not be shared directly with sites and would be cleared when permission is revoked or site data in general is cleared.
Commands a page can send to the device after it has been granted access a page may also be able to access any of the other sensitive information stored by the device. For the reasons mentioned in 7. Security considerations it is impractical and undesirable to attempt to prevent a page from accessing this information.
Implementations should provide users with complete control over which
devices a site can access and not grant device access without user
interaction. This is the intention of the requestPort
()
method. This prevents a site from silently enumerating and collecting
data from all connected devices. This is similar to the file picker UI.
A site has no knowledge of the filesystem, only the files or
directories that have been chosen by the user. An implementation could
notify the user when a site is using these permissions with an
indicator icon appearing in the tab or address bar.
Implementations that provide a "private" or "incognito" browsing mode should ensure that permissions from the user's normal profile do not carry over to such a session and permissions granted in this session are not persisted when the session ends. An implementation may warn the user when granting access to a device in such as session as, similar to entering identifying information by hand, device identifiers and other unique properties available from communicating with the device mentioned previously can be used to identify the user between sessions.
Users may be surprised by the capabilities granted by this API if they do not understand the ways in which granting access to a device breaks traditional isolation boundaries in the web security model. Security UI and documentation should explain that granting a site access to a device could give the site full control over the device and any data contained within.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: