Security fixes are applied to the current release on main. Older binaries are not supported; install the latest release from https://shell.online/install before reporting an issue.
Please do not open a public issue for a suspected vulnerability. Use GitHub's Security → Report a vulnerability flow in this repository so details remain private. Include the affected version, impact, reproduction steps, and any suggested mitigation.
You should receive an acknowledgement within three business days. We will validate the report, coordinate a fix and disclosure with you, and credit you unless you prefer to remain anonymous.
- An interactive share URL intentionally grants anonymous view and input access. A recipient typing into that shared process is not an authorization bypass.
- A session created with
--read-onlygrants anonymous view access only. Browser input that reaches a read-only session must be rejected by the Worker; any bypass is in scope. - By default, every session created by the current CLI encrypts terminal payloads between the CLI and browser. Cloudflare still receives frame opcodes and traffic/lifecycle metadata. Plaintext disclosure to the relay, nonce/key reuse that compromises confidentiality, acceptance of modified ciphertext, or cross-language key-derivation incompatibility is in scope. Older clients that predate default E2EE are unsupported and should be upgraded.
--no-e2eeis an explicit opt-out for compatibility or debugging. In that mode HTTPS/WSS protects each transport hop, but terminal payloads intentionally pass through Cloudflare in memory. Relay access to that plaintext is expected behavior and the CLI must label it clearly; accidental fallback from the default encrypted mode is in scope.- E2EE access remains a bearer capability. Possession of both the complete salted URL and its browser password intentionally grants decryption. Relay-side dropping, delaying, and replaying of valid ciphertext are documented protocol limits rather than confidentiality claims.
- Session passwords are kept in a per-account session vault. The accounts service stores each password only sealed to the account's vault public key (ephemeral ECDH P-256, HKDF-SHA256, AES-256-GCM bound to the session id and recipient). The matching private key is stored encrypted under a random vault key, and the vault key is wrapped only by a 160-bit recovery key the service never receives. Any way for the accounts service, the relay, or a copy of the database to open a sealed password, the private key, or the vault key is in scope, as is substituting a vault public key without the browser or the CLI refusing it. Two points are trust-on-first-use by design: the first key seen for a colleague, and the account key a machine linked before the vault existed learns on its next session. The web app served by shell.online performs the unlock and is trusted to, as it is trusted with a typed session password.
- Input typed into a session from the browser is recorded in the team's audit log and encrypted in the browser to the team's audit public key (ephemeral ECDH P-256, HKDF-SHA256, AES-256-GCM bound to the organization, session, entry kind, time and author). The matching private key reaches each member sealed to their session vault by a teammate's own vault key, and the service stores only the public key, the sealed copies and ciphertext. Any way for the accounts service, the relay, or a copy of the database to read audit input text or the team audit key is in scope, as is the service substituting a team key that members then encrypt to. Metadata stays readable by the service by design: who acted, in which session, the entry kind and time, and the lifecycle entries the service writes itself. Trust-on-first-use applies to a teammate's first-seen vault key, and the web app served by shell.online performs the encryption. Entries recorded before encryption existed may remain readable until an owner's or admin's browser encrypts them in place, and in database backups taken before then until those expire.
- The CLI generates an eight-character base64url password with 48 bits of entropy when no password is supplied. This is an explicit convenience/security tradeoff for task-bound shares, not a claim of passphrase-strength protection.
SHELL_ONLINE_E2EE_PASSWORDaccepts a longer unique password for sensitive or long-lived sessions; recipients should receive the URL and password through separate channels when appropriate. - Persistent state files and Docker state volumes intentionally contain the host credential, browser password, and E2EE key material. Files created by shell.online must be owner-only. Live rotation replaces the salt, password, and key in that state before the host changes ciphers; the stable session path remains the same. Disclosure caused by publishing, broadly mounting, or backing up that state outside shell.online is not a product vulnerability.
- Active-session records in the per-user local control directory intentionally retain the browser password so
shell password <ID>can recover it. Humanshell listoutput does not print every password;--jsondeliberately includes them for agents. The directory and records must remain owner-only and are deleted when their processes close. shell password rotate <ID>changes only future access: it cannot erase output a viewer already received. The host rejects old-key input as soon as it swaps ciphers, the relay disconnects current viewers without receiving either credential, and the account registry atomically replaces sealed copies from the previous generation. Old URL/password pairs may still connect to an anonymous relay socket but cannot authenticate later encrypted frames.- Removing a team member deletes every password copy still sealed to that account, but cannot make them forget a password or terminal output they already received. Rotate each active session that person could open when immediate revocation matters.
- Reports about leaked links are actionable when shell.online itself disclosed or made them predictable; links forwarded or published by their owner are not a product vulnerability.
- Availability reports should demonstrate a way to bypass the configured rate, frame-size, audience, or lifetime limits.
- The statistics dashboard is private and password-protected. Do not test it with credential stuffing or high-volume traffic.
When testing, use your own machine and sessions. Do not access another person's terminal, retain terminal contents, or degrade the public service.