Join our Newsletter — 33% off our NHI Course
Home› Guides› Deno and Bun Runtime Security Guide: Permissions, Install…
Guide Application Security

Deno and Bun Runtime Security Guide: Permissions, Install Scripts and Supply Chain Defaults

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 27 October 2026 12 min read
On this page

Deno and Bun both run npm packages, but they make opposite bets on security: Deno denies file, network, environment and subprocess access unless you grant it, while Bun runs with the full authority of its user and focuses its protection on what gets installed. That difference changes how a compromised dependency behaves, what a leaked token is worth and which controls a platform team has to add. This guide compares the two runtimes' security models, permission flags, install-script handling, release-age gates, lockfile controls and credential handling, and gives a hardening baseline for each. It is for developers, platform engineers and AppSec teams adopting either runtime for services, CLIs or CI. Facts come from the Deno and Bun documentation as checked on 27 October 2026, when the latest releases were Deno 2.9.8 and Bun 1.4.4. For the Node.js equivalent see the Node.js Permission Model Guide.

Key takeaways

  • Deno is deny-by-default; Bun is not. Deno code has no I/O without --allow-* flags; Bun's documentation describes no runtime permission flags, so every dependency has the process user's full access.
  • Some Deno grants equal "allow all". Deno's own docs say --allow-run and --allow-ffi bypass the sandbox, and --allow-run=deno lets code start a fresh, unrestricted Deno process.
  • Both refuse most install scripts by default. Deno runs npm lifecycle scripts only with --allow-scripts; Bun runs them only for packages in trustedDependencies or its built-in allow list.
  • Both can delay brand-new releases. Deno 2.9 enforces a 24-hour minimum dependency age by default; Bun's minimumReleaseAge is opt-in and set in seconds.
  • Neither is a sandbox for hostile code. Deno lets any code run at the same privilege through eval and dynamic import, and recommends OS sandboxing or a VM for untrusted code; Bun relies on those layers entirely.

How identity and access work in Deno and Bun

A JavaScript runtime acts with the identity of the OS user that starts it, so every package in its module graph can use that user's files, environment variables and network position. The supply chain question is how much of that authority the runtime hands to dependency code.

AreaDeno (2.9)Bun (1.4)
Default runtime accessNo file, network, env, subprocess, FFI or system info accessFull access of the OS user
Granting access--allow-read, --allow-write, --allow-net, --allow-env, --allow-run, --allow-sys, --allow-ffi, --allow-import, scoped per resource; --deny-* overridesNot applicable
Denied operationThrows Deno.errors.NotCapable, or prompts on an interactive terminalSucceeds if the OS allows it
npm lifecycle scriptsNot run by default; deno install --allow-scripts=npm:pkgRun only for trustedDependencies or the default trusted list
New-release gateMinimum dependency age on by default (24 hours)minimumReleaseAge opt-in
Publishing-trust checktrust-policy=no-downgrade in .npmrcPluggable security scanner ([install.security])
Vulnerability auditdeno auditbun audit
.env handlingLoaded only when you pass --env-fileLoads .env, .env.<NODE_ENV> and .env.local files automatically
On-the-fly package installsRemote imports limited to default HTTPS hosts unless --allow-import changes themAuto-install when no node_modules exists (install.auto = "auto" by default)

Deno's security principles

Deno's documentation sets out principles that define what its sandbox does and does not promise:

  • Same thread, same privilege: all modules in a thread share one permission set; a dependency cannot be given less than your own code.
  • No limit on code at the same privilege: eval, new Function, dynamic imports and workers can run arbitrary code with the caller's permissions.
  • Static imports skip permission checks: modules in the initial static graph (local files, npm, JSR and remote URLs) load without --allow-read or --allow-net; non-literal import(someVariable) is checked at runtime.
  • No escalation without consent: code cannot gain permissions except through a prompt or a launch flag.

Admin roles and privileged access

Neither runtime has accounts or roles. Privilege is set by whoever controls the launch command, the configuration files and the install step.

Deno grants that act like administrator rights

GrantWhy it is dangerousSafer alternative
-A / --allow-allTurns the sandbox off; Deno says it has the same security properties as Node.jsExplicit scoped flags
--allow-run (unscoped)Subprocesses run outside the sandbox; spawning with LD_* or DYLD_* variables requires the unscoped flag--allow-run=git for named trusted programs
--allow-run=deno or a shellCode can start a new Deno process with --allow-all and escape entirelyNever grant to sandboxed code
--allow-ffiNative libraries make system calls directly, ignoring all flagsScope to a library path and treat as full trust
--allow-write plus --allow-runCode can overwrite an allowed binary, then run itNo write access to directories holding allowed executables
DENO_PERMISSION_BROKER_PATHHands every permission decision to an external broker; all --allow-* and --deny-* flags and prompts are ignoredOnly for central policy services that are audited and highly available

The permission broker is worth understanding for platform teams: Deno sends each check as a versioned JSON request with process ID, request ID, timestamp, permission and value, and expects allow or deny. If the connection fails or messages are malformed, Deno terminates the process rather than fall back to the flags.

Bun's privileged configuration

  • trustedDependencies in package.json: each entry is a package allowed to run shell commands at install time. Defining the field replaces Bun's default list rather than extending it, and [] trusts nothing.
  • bunfig.toml: controls auto-install, release age, the security scanner and .env loading. Treat it as security configuration with code-owner review.
  • Who runs bun: because Bun has no permission flags, the OS user, container and network policy are the only runtime boundary.

Integrations, API tokens and service accounts

The credentials that matter most are registry tokens, cloud keys and CI tokens. The Shai-Hulud npm Worm used a preinstall script that installed the Bun runtime and launched its payload with it, harvesting GitHub, npm and cloud credentials from files, environment variables and metadata services (ATT&CK T1552.001 and T1552.005). Install-script controls in Deno and Bun target exactly that step.

  • Registry credentials: Bun reads ~/.npmrc, the project .npmrc and NPM_CONFIG_TOKEN or BUN_CONFIG_TOKEN. Keep tokens out of project .npmrc files, use read-only tokens for installs and short-lived publish credentials. See the npm Tokens Security Guide.
  • Environment secrets in Deno: grant only named variables, for example --allow-env=DATABASE_URL, or use --ignore-env to make other reads return undefined. Reading /proc/**/environ through a symlink requires --allow-env.
  • Environment secrets in Bun: automatic .env loading means a stray .env.local in a container image becomes live configuration. Disable it in production with --no-env-file or env = false in bunfig.toml and inject secrets from a secrets manager.
  • Network egress: in Deno, scope --allow-net to the hosts a service calls (--allow-net=api.example.com:443); in Bun, enforce egress at the network layer.
  • Dropping access after start-up (Deno): read configuration, then call Deno.permissions.revoke({ name: "read" }) so later code, including dependencies, cannot reread secret files.

See the Secret Sprawl Measurement Guide for finding credentials that have already spread into repositories and images.

Install-time and supply chain controls

ControlDenoBun
Lifecycle scriptsIgnored with a warning; allow per package with --allow-scripts=npm:duckdbAllowed for trustedDependencies or the default list (npm sources only; git:, file: and github: sources must be listed explicitly); --ignore-scripts or install.ignoreScripts = true disables all
Release ageOn by default since 2.9 (24 hours); "minimumDependencyAge": "P3D" in deno.json or --minimum-dependency-ageminimumReleaseAge = 259200 (seconds) in bunfig.toml, with minimumReleaseAgeExcludes; applies only to new resolution
Frozen installs"lock": { "frozen": true } or --frozen; deno ci--frozen-lockfile or bun ci; not automatic in CI
Publishing-trust downgradetrust-policy=no-downgrade rejects versions published with a weaker method (plain, provenance, trusted publishing, staged) than the locked baselineNot documented; use a scanner
Pre-install scanningNot built in[install.security] scanner = "..."; fatal advisories stop installs, warn exits in CI
Offline or hermetic runs"vendor": true, deno run --cached-onlyinstall.auto = "disable" to stop on-the-fly installs

Bun's auto-install deserves attention: when no node_modules exists, Bun installs imported packages on the fly into a global cache, and a package with no version in any package.json resolves to latest. Bun turns auto-install off when a security scanner is configured, and you can set install.auto = "disable". See the npm Install-Time Security Guide for the shared registry risks.

Logging and detection

SignalRuntimeUse
NotCapable errorsDenoCatch and log the permission and resource; new values after a dependency update are a red flag
Permission broker requestsDenoCentral log of every permission decision across processes
"Ignored build scripts" warningsDenoInventory packages that wanted to run install scripts
Scanner fatal/warn resultsBunBlocked installs in CI with advisory details
deno audit / bun audit outputBothKnown vulnerable versions from the lockfile
Lockfile diffsBothNew packages, changed integrity hashes, new trustedDependencies or trust fields in deno.lock

Metrics:

  • Deno services launched without -A, unscoped --allow-run or --allow-ffi (target 100 per cent).
  • Number of packages allowed to run install scripts per project (falling).
  • Projects with a release-age gate of at least 24 hours and a frozen lockfile in CI.
  • Bun projects with a configured security scanner and .env auto-loading disabled in production.
  • Time from an audit finding to a merged fix.

Hardening baseline

Deno

  1. Declare permissions in deno.json or the launch command, scoped to paths, hosts and variables; never ship -A.
  2. Run non-interactive services with --no-prompt so a missing grant fails instead of waiting.
  3. Commit deno.lock, set "lock": { "frozen": true }, and use deno ci or deno install --frozen in pipelines.
  4. Keep the default minimum dependency age or raise it (for example P3D), and set trust-policy=no-downgrade.
  5. Allow lifecycle scripts only per package, after review.
  6. Override default import hosts with an explicit --allow-import list if you do not use them; imports are HTTPS only.
  7. Revoke read and env permissions after initialisation where possible.
  8. For untrusted code, follow Deno's guidance: limited permissions, --frozen and --cached-only, a worker with a reduced permission set, OS sandboxing, and a VM or microVM such as gVisor or Firecracker.

Bun

  1. Run as a non-root user in a container with a read-only file system and egress policy; this is the runtime boundary.
  2. Set trustedDependencies explicitly (or []) so the default allow list is not trusted implicitly.
  3. Configure [install.security] with a scanner and set minimumReleaseAge, for example three days.
  4. Use bun ci or --frozen-lockfile in every pipeline.
  5. Set install.auto = "disable" in production images and CI.
  6. Disable .env auto-loading in production and pull secrets from a manager.
  7. Run bun audit in CI.

Rolling it out

  1. Discover: in Deno, run the service once in a terminal without flags and record each permission prompt; turn the answers into scoped flags in deno.json.
  2. Pilot: enforce with --no-prompt in staging and watch for NotCapable errors; in Bun, enable the scanner and release age on one repository first.
  3. Enforce in CI: add policy checks that fail a build on -A, unscoped --allow-run, new trustedDependencies entries or a missing frozen lockfile.
  4. Roll back: widen a single scoped flag or exclude one package from the age gate, never revert to -A or an empty age gate wholesale.

Choosing between them

  • Running third-party scripts or plugins: Deno's permission flags give a meaningful extra layer; Bun needs OS isolation for the same result.
  • Node.js compatibility and speed of migration: Bun runs most Node.js code unchanged, but you inherit Node.js-style full authority; plan container and network controls first.
  • Small teams: Deno's defaults (deny I/O, ignore scripts, 24-hour age gate) give more protection with no configuration. Large organisations should standardise either runtime with a shared baseline config and policy checks on launch flags.

Common misconfigurations

  • deno run -A in production: usually added to "make it work"; it erases every advantage Deno has over Node.js.
  • Granting --allow-run to call one tool: unscoped, it lets code run anything, including a new unrestricted Deno process.
  • Assuming Deno blocks dependency loading: static imports load without permission checks, so a malicious module in the graph still runs, just with your granted permissions.
  • Extending Bun's trusted list by accident: adding one package to trustedDependencies drops the default list, breaking builds; teams then add many packages without review.
  • Relying on Bun auto-install in scripts: a one-off script in a directory without node_modules pulls latest from the registry at run time.
  • Shipping .env files in Bun images: automatic loading turns a forgotten file into production configuration, including old credentials.

Framework mapping

Control areaNIST SP 800-53 Rev. 5NIST CSF 2.0Other
Runtime least privilege (Deno flags, Bun OS isolation)AC-6, CM-7, SC-39PR.PS-01ATT&CK T1059.007 JavaScript
Install-script control, release age, scanningCM-7(5), SR-3PR.PS-06, GV.SC-07ATT&CK T1195.001; OWASP ASVS 5.0 V15.1.2
Lockfiles and integritySI-7PR.PS-02OWASP ASVS 5.0 V15.2.5
Credential exposureIA-5PR.AA-01ATT&CK T1552.001, T1552.005
Permission and install loggingAU-12DE.CM-09CIS Controls v8.1 16.4

Practitioner checklist

  • Inventory which services and CI jobs run on Deno or Bun, with their versions.
  • Deno: no -A, no unscoped --allow-run, no --allow-run=deno, and --allow-ffi treated as full trust.
  • Deno: frozen lockfile, minimum dependency age of at least 24 hours, trust-policy=no-downgrade.
  • Bun: explicit trustedDependencies, a configured security scanner and minimumReleaseAge.
  • Bun: bun ci in pipelines, auto-install disabled and .env auto-loading off in production.
  • Both: registry tokens out of project files, read-only for installs, short-lived for publishing.
  • Both: deno audit or bun audit in CI with a fix SLA.
  • Both: non-root containers with egress controls; VMs or microVMs for untrusted code.

Standards and references

Related NHI Mgmt Group resources: Node.js Security Guide · Malicious Package and Dependency Confusion Guide · Shai-Hulud npm Worm Guide · Container Runtime Security Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 27 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org