Deno 1.36: More flexible security and expanded testing APIs
At the core of Deno’s design goals is flexible and robust runtime security. With
Deno 1.36, we’re expanding your security options even further with --deny-*
flags. Along with our existing --allow-*
flags, you can now configure both
allow and deny lists for network communication, file system access, and other
potentially sensitive APIs.
Along with these security features, you’ll find improved testing and benchmarking APIs, more robust Node.js and npm package support, language server improvements, and much more in 1.36.
If you have Deno installed, you can upgrade to version 1.36 in your terminal with:
deno upgrade
Read on to learn more about the latest features and fixes available in Deno 1.36!
Deno 1.36 at a glance
-
🔐
More flexible security options for Deno programs
Secure your programs at runtime with
--deny-*
flags, which enable you to configure deny lists for domains, file paths, and other resources to which access should be restricted. - 🧪 Expanded options for testing and benchmarking New test result formatters, support for Node.js testing APIs, and more granular benchmarking.
-
⬢
Node.js compatibility improvements
Run npm package scripts that aren’t configured as binaries, use all
node:os
APIs with our latest polyfill, call native code add-ons withprocess.dlopen
, and more. - 🏝️ Quality of life improvements Small features and fixes like better error messages, language server improvements, and more forgiving WebSocket APIs.
- 🙏 Open source community contributions We couldn’t build Deno without support from our community! These are the awesome folks outside the core team that have landed contributions to Deno 1.36.
More flexible security options for Deno programs
The Deno runtime offers security by default, empowering developers to
opt in to letting
their code and dependencies make network requests, access the filesystem, or use
other potentially hazardous APIs with the --allow-*
CLI flags.
These options are and remain useful, but they are a bit inflexible in certain
scenarios. You can either give your program unfettered access to a given
feature, or you would have to configure specific domains or directories the
--allow-*
options would enable. There’s no way to open up the sandbox with
just some domains or file paths excluded. In Deno 1.36, we introduce the
--deny-*
family of runtime flags to enable more flexible permissions for your
Deno programs.
For example, if you’d like your program to have access to any URL it would like, except for a few specific IP addresses and domains, you can use:
$ deno run --allow-net --deny-net=api.evil.com mod.ts
For instance, if you want to give your program access to the whole filesystem,
except the /etc
directory:
$ deno run --allow-read --deny-read=/etc --allow-write --deny-write=/etc mod.ts
Every CLI flag that previously had an --allow-*
option will now have
corresponding --deny-*
options too. The deny flags have higher precedence than
the allow flags, so if you use both on the same domains or a file path, the
access will still be denied:
$ deno run --allow-read=foo.txt --deny-read=foo.txt mod.ts
error: PermissionDenied: Requires read access to "foo.txt"...
A special shoutout is called for here to Asher Gomez and Nayeem Rahman, who led the implementation of this new feature. Thanks a ton for your contributions!
We intend to further improve and expand the capabilities of the permission system in the future. One of the features we are looking to implement is the ability to use pre-defined permissions from a configuration file - follow this issue for updates.
We’d love to hear your feedback on the new --deny-*
flags, and about any other
needs you have related to the permission system. Let us know in
Discord, during one of our live events (also found on
the Discord), or by raising an issue
in GitHub.
Expanded options for testing and benchmarking
Deno 1.36 also introduces improvements to testing and benchmarking in your
applications. On the testing side, you can now output the results of
deno test
runs using
new, custom formatters.
JUnit reporter
Having a machine-readable test report is crucial in automated QA of your tests
and keeping large codebases maintainable. The JUnit XML format can be consumed
natively by many services like GitLab, CircleCI, Jenkins or BuildPulse. Starting
with Deno 1.36, you can pass the --reporter=junit
flag to deno test
to get
structured report data:
# Print a JUnit report to the terminal so you can process it further…
$ deno test --reporter=junit
# …or write it directly to a file alongside the default human-readable output.
deno test --junit-path=test_report.xml
Dot reporter
For folks who prefer short and sweet test reports we added a dot
reporter that
provides a concise output, removing a lot of noise.
deno test --reporter=dot
node:test
polyfill
If you are coming to Deno from recent releases of Node.js, you can now use the
built-in test API from Node 20 in addition to
Deno.test
or
describe
/it
API from the standard library.
Check it out for yourself in Deno with a simple test harness like the one below.
import assert from "node:assert";
import test from "node:test";
test("synchronous passing test", (t) => {
// This test passes because it does not throw an exception.
assert.strictEqual(1, 1);
});
test("asynchronous passing test", async (t) => {
// This test passes because the Promise returned by the async
// function is not rejected.
assert.strictEqual(1, 1);
});
Not all API surface area has been covered yet, so if you hit a problem we would greatly appreciate a bug report.
deno bench
improvements
The granularity and precision of benchmarking in Deno have been improved with
the addition of new functionality to
Deno.bench
.
In previous releases of Deno, the Deno.bench
function gave surprising results
for the first bench case run due to a phenomenon called “JIT bias” - the V8
engine was over-optimizing benchmarked function and then bailing out of the
optimization on the subsequent functions, leading to non-representative results.
This has now been fixed, by running a “warmup” function before user defined
bench cases.
Additionally, you can now use Deno.BenchContext.start
and
Deno.BenchContext.end
to tell the benchmarking tool about the critical section
you want to measure. Everything outside of the section between these two calls
will be excluded from the measurement.
import { readAll } from "https://deno.land/std@0.197.0/streams/mod.ts";
Deno.bench("foo", async (b) => {
// Open a file that we will act upon.
const file = await Deno.open("a_big_data_file.txt");
// Tell the benchmarking tool that this is the only section you want
// to measure.
b.start();
// Now let's measure how long it takes to read all of the data from the file.
await readAll(file);
// End measurement here.
b.end();
// Now we can perform some potentially time-consuming teardown that will not
// taint out benchmark results.
file.close();
});
We also updated
deno bench
output to
include information about iterations per second:
Node.js compatibility improvements
Run scripts from npm that aren’t configured as binaries
You can now run scripts from npm packages that are not configured in a package’s
bin
property in package.json.
Example:
deno run -A npm:somepackage@0.0.1/foo/cli.mjs
process.dlopen
is available
You can now use
process.dlopen
API to load native addons for Node.js.
AsyncLocalStorage
up to 3.5x faster
We optimized the implementation of
AsyncLocalStorage
from node:async_hooks
module. Our benchmarks show that it’s up to 3.5x faster
than in Deno v1.35.3.
node:os
is fully polyfilled
The getPriority
, setPriority
, and userInfo
functions of
the Node.js os
module are now available,
making node:os
module fully pollyfilled.
Learn more about using Node.js built-ins in the Deno Manual.
Example:
import { getPriority } from "node:os";
console.log(getPriority());
Quality of life improvements
Below, we’ve included a few other features and bug fixes that made their way into the release that we think will make a meaningful impact in your day-to-day work.
deno compile --no-terminal
A new --no-terminal
flag can now be used with
deno compile
. If the
compiled binary is run on Windows, this flag will prevent the terminal window
from being opened.
Deno.createHttpClient.allowHost
The unstable
Deno.createHttpClient
now supports the allowHost
option, making it possible to specify a Host
header for a fetch
request.
const client = Deno.createHttpClient({
allowHost: true,
});
const res = await fetch("http://example.com", {
headers: {
"host": "myhost.com",
},
client,
});
WebSocket
API
Allow HTTP(S) URLs in You can now use http:
and https:
URLs in the
WebSocket
Web API,
Deno will automatically adjust the protocol to use ws:
or wss:
respectively:
const url = new URL("https://example.com");
const ws = new WebSocket(url);
console.log(ws.url);
// wss://example.com
Retry failed module downloads
Deno is now more resilient when downloading dependencies. Connecting to remote hosts can always fail due to intermittent connection problems or spurious errors on the remote server.
On such occasions, Deno will wait a short amount of time and retry the download, making CI pipelines more reliable and reducing the need to re-run commands.
Deno.Command
API
More helpful errors in Before 1.36, if you tried to spawn a subprocess for a binary that was
non-existent with Deno.Command
, you
were presented with an unhelpful error message:
$ deno
> new Deno.Command("this-binary-does-not-exist").spawn();
Uncaught NotFound: No such file or directory (os error 2)
at spawnChildInner (ext:runtime/40_process.js:162:17)
at spawnChild (ext:runtime/40_process.js:182:10)
at Command.spawn (ext:runtime/40_process.js:440:12)
at <anonymous>:2:48
With Deno 1.36, the error message will include the name of the binary that was not found:
$ deno
> new Deno.Command("this-binary-does-not-exist").spawn();
Uncaught NotFound: Failed to spawn: this-binary-does-not-exist: No such file or directory (os error 2)
at spawnChildInner (ext:runtime/40_process.js:162:17)
at spawnChild (ext:runtime/40_process.js:182:10)
at Command.spawn (ext:runtime/40_process.js:440:12)
at <anonymous>:2:48
LSP improvements
Deno 1.36 contains a plethora of fixes and improvements to the LSP. This should make using Deno with an editor that supports an LSP (like Visual Studio Code) significantly more pleasant. Here are a few of the changes:
- Better diagnostics when
Deno
namespace is missing - Auto-discovery of
deno.json
is more reliable - LSP respects
"exclude"
setting fromdeno.json
node:
specifiers from import maps are properly handled- Symlinked configuration files are now supported
- Diagnostics are no longer flickering
Still want to learn more?
Believe it or not, the changes listed above still don’t tell you everything that got better in 1.36. You can view the full list of pull requests merged in Deno 1.36 on GitHub here.
Thank you to our community contributors!
We couldn’t build Deno without the help of our community! Whether by answering questions in our community Discord server or reporting bugs, we are incredibly grateful for your support. In particular, we’d like to thank the following people for their contributions to Deno 1.36.
- Elian Cordoba
- Felipe Baltor
- Jakub Jirutka
- JasperVanEsveld
- Kyoh
- Marcos Casagrande
- Martin Fischer
- Ricardo Iván Vieitez Parra
- Roj
- Vedant Pandey
- VlkrS
- await-ovo
- idanran
- puffyCid
- sigmaSd
- sitogi
- solach
- liruifengv
Would you like to join the ranks of Deno community contributors? Check out our contribution docs here, and we’ll see you on the list next time.
Thank you for catching up with our 1.36 release, and we hope you love building with Deno!
🍋 Did you know? Fresh just got fresher.
Be sure to check out the release notes for Fresh 1.3, the latest iteration of the next-gen web framework for Deno.