fix(cli): honor PASEO_PASSWORD env var (fix #776) (#782)

* fix(cli): honor PASEO_PASSWORD env var (fix #776)

The CLI only extracted passwords from `tcp://host?password=` URIs, so
`PASEO_PASSWORD=xxxx paseo ls` connected without auth and was rejected
by the daemon with "Password required". Fall back to the env var when
the host (TCP or otherwise) carries no password.

* docs: document PASEO_PASSWORD env var as a CLI auth source

Pairs with the CLI fix in this branch — the "Connecting with a password"
section now lists both the tcp URI query and the env-var fallback, with
their precedence. Also clarify the dual role (daemon vs CLI) of
PASEO_PASSWORD in the env var reference.
This commit is contained in:
Mohamed Boudra
2026-05-06 23:22:22 +08:00
committed by GitHub
parent c36f4cfee4
commit 55ec13f153
3 changed files with 65 additions and 14 deletions
+6 -1
View File
@@ -218,7 +218,12 @@ export function resolveDaemonTarget(host: string): DaemonTarget {
export function resolveDaemonPassword(host: string): string | undefined { export function resolveDaemonPassword(host: string): string | undefined {
const trimmed = host.trim(); const trimmed = host.trim();
return trimmed.startsWith("tcp://") ? parseConnectionUri(trimmed).password : undefined; if (trimmed.startsWith("tcp://")) {
const fromUri = parseConnectionUri(trimmed).password;
if (fromUri) return fromUri;
}
const fromEnv = process.env.PASEO_PASSWORD;
return fromEnv && fromEnv.length > 0 ? fromEnv : undefined;
} }
/** /**
@@ -143,14 +143,49 @@ console.log("=== CLI IPC Target Helpers ===\n");
} }
{ {
console.log("Test 10: daemon password resolution uses only the TCP URI query"); console.log("Test 10: daemon password resolution prefers TCP URI query, falls back to env");
assert.strictEqual( const previousEnv = process.env.PASEO_PASSWORD;
resolveDaemonPassword("tcp://example.com:6767?ssl=true&password=query-secret"), try {
"query-secret", delete process.env.PASEO_PASSWORD;
); assert.strictEqual(
assert.strictEqual(resolveDaemonPassword("tcp://missing.example:6767"), undefined); resolveDaemonPassword("tcp://example.com:6767?ssl=true&password=query-secret"),
assert.strictEqual(resolveDaemonPassword("example.com:6767"), undefined); "query-secret",
console.log("✓ daemon password resolution uses only the TCP URI query\n"); );
assert.strictEqual(resolveDaemonPassword("tcp://missing.example:6767"), undefined);
assert.strictEqual(resolveDaemonPassword("example.com:6767"), undefined);
process.env.PASEO_PASSWORD = "env-secret";
assert.strictEqual(
resolveDaemonPassword("tcp://example.com:6767?ssl=true&password=query-secret"),
"query-secret",
"URI password should take precedence over env var",
);
assert.strictEqual(
resolveDaemonPassword("tcp://missing.example:6767"),
"env-secret",
"TCP host without query password should fall back to env var",
);
assert.strictEqual(
resolveDaemonPassword("example.com:6767"),
"env-secret",
"Bare host should pick up env var password",
);
assert.strictEqual(resolveDaemonPassword("localhost:6767"), "env-secret");
process.env.PASEO_PASSWORD = "";
assert.strictEqual(
resolveDaemonPassword("localhost:6767"),
undefined,
"Empty env var should be treated as unset",
);
} finally {
if (previousEnv === undefined) {
delete process.env.PASEO_PASSWORD;
} else {
process.env.PASEO_PASSWORD = previousEnv;
}
}
console.log("✓ daemon password resolution prefers TCP URI query, falls back to env\n");
} }
console.log("=== All CLI IPC target tests passed ==="); console.log("=== All CLI IPC target tests passed ===");
+16 -5
View File
@@ -123,18 +123,29 @@ After setting a password, restart the daemon for the change to take effect.
### Connecting with a password ### Connecting with a password
The CLI reads the password from the TCP URI automatically: The CLI picks up a password from, in order:
```bash 1. The `password` query parameter on a `tcp://` host URI:
paseo --host "tcp://192.168.1.10:6767?password=my-secret" ls
``` ```bash
paseo --host "tcp://192.168.1.10:6767?password=my-secret" ls
```
2. The `PASEO_PASSWORD` environment variable, used as a fallback when the host carries no embedded password (works for `localhost:6767`, bare `host:port`, or `tcp://` hosts without a `password=` query):
```bash
PASEO_PASSWORD=my-secret paseo ls
PASEO_PASSWORD=my-secret paseo --host 192.168.1.10:6767 ls
```
A `password=` in the URI always wins over the env var, so you can keep `PASEO_PASSWORD` set globally and still target a different daemon by spelling its password into the URI.
In the mobile app, enter the password in the direct connection setup screen. In the mobile app, enter the password in the direct connection setup screen.
## Common env vars ## Common env vars
- `PASEO_HOME` — set Paseo home directory - `PASEO_HOME` — set Paseo home directory
- `PASEO_PASSWORD` — require a password to connect (plaintext, hashed at startup) - `PASEO_PASSWORD` — on the daemon, the password to require (plaintext, hashed at startup); on the CLI, the password used to connect when the host URI doesn't include one
- `PASEO_LISTEN` — override `daemon.listen` - `PASEO_LISTEN` — override `daemon.listen`
- `PASEO_HOSTNAMES` — override/extend `daemon.hostnames` - `PASEO_HOSTNAMES` — override/extend `daemon.hostnames`
- `PASEO_ALLOWED_HOSTS` — deprecated alias for `PASEO_HOSTNAMES` - `PASEO_ALLOWED_HOSTS` — deprecated alias for `PASEO_HOSTNAMES`