Skip to content

Commit 2b49a04

Browse files
committed
fix: prevent password and connection string leaking in --help output
bpaf's env() renders the current env var value in --help, e.g. [env:PGPASSWORD = "secret"]. Remove env() from sensitive fields (password, connection_string) and use from_env() merge in the CLI path instead, matching the LSP approach.
1 parent 704f5e5 commit 2b49a04

3 files changed

Lines changed: 34 additions & 14 deletions

File tree

crates/pgls_cli/src/commands/mod.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,10 @@ pub enum PgLSCommand {
196196
/// Writes to stdout by default, or to a file if --output is specified.
197197
#[bpaf(command("schema-export"))]
198198
SchemaExport {
199-
/// PostgreSQL connection string (e.g., postgres://user:pass@host/db)
200-
#[bpaf(
201-
env("DATABASE_URL"),
202-
long("connection-string"),
203-
short('c'),
204-
argument("URL")
205-
)]
206-
connection_string: String,
199+
/// PostgreSQL connection string (e.g., postgres://user:pass@host/db).
200+
/// Can also be set via the `DATABASE_URL` environment variable.
201+
#[bpaf(long("connection-string"), short('c'), argument("URL"), optional)]
202+
connection_string: Option<String>,
207203

208204
/// Output file path for the JSON schema (defaults to stdout if not specified)
209205
#[bpaf(long("output"), short('o'), argument("PATH"))]

crates/pgls_cli/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ impl<'app> CliSession<'app> {
144144
connection_string,
145145
output,
146146
} => {
147+
let connection_string = connection_string
148+
.or_else(|| std::env::var("DATABASE_URL").ok())
149+
.ok_or_else(|| {
150+
CliDiagnostic::missing_argument(
151+
"--connection-string (or DATABASE_URL env var)",
152+
"schema-export",
153+
)
154+
})?;
147155
let runtime = tokio::runtime::Runtime::new().map_err(CliDiagnostic::io_error)?;
148156
runtime.block_on(commands::schema_export::run_schema_export(
149157
&connection_string,
@@ -197,6 +205,16 @@ impl<'app> CliSession<'app> {
197205
configuration.merge_with(cli_config);
198206
}
199207

208+
// Env vars take highest priority — merge last so they override everything.
209+
if let Some(env_db) = pgls_configuration::database::PartialDatabaseConfiguration::from_env()
210+
{
211+
let env_config = PartialConfiguration {
212+
db: Some(env_db),
213+
..Default::default()
214+
};
215+
configuration.merge_with(env_config);
216+
}
217+
200218
Ok(configuration)
201219
}
202220

crates/pgls_configuration/src/database.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,35 @@ use serde::{Deserialize, Serialize};
1111
pub struct DatabaseConfiguration {
1212
/// A connection string that encodes the full connection setup.
1313
/// When provided, it takes precedence over the individual fields.
14-
#[partial(bpaf(env("DATABASE_URL"), long("connection-string")))]
14+
/// Can also be set via the `DATABASE_URL` environment variable.
15+
#[partial(bpaf(long("connection-string")))]
1516
pub connection_string: Option<String>,
1617

1718
/// The host of the database.
1819
/// Required if you want database-related features.
1920
/// All else falls back to sensible defaults.
20-
#[partial(bpaf(env("PGHOST"), long("host")))]
21+
/// Can also be set via the `PGHOST` environment variable.
22+
#[partial(bpaf(long("host")))]
2123
pub host: String,
2224

2325
/// The port of the database.
24-
#[partial(bpaf(env("PGPORT"), long("port")))]
26+
/// Can also be set via the `PGPORT` environment variable.
27+
#[partial(bpaf(long("port")))]
2528
pub port: u16,
2629

2730
/// The username to connect to the database.
28-
#[partial(bpaf(env("PGUSER"), long("username")))]
31+
/// Can also be set via the `PGUSER` environment variable.
32+
#[partial(bpaf(long("username")))]
2933
pub username: String,
3034

3135
/// The password to connect to the database.
32-
#[partial(bpaf(env("PGPASSWORD"), long("password")))]
36+
/// Can also be set via the `PGPASSWORD` environment variable.
37+
#[partial(bpaf(long("password")))]
3338
pub password: String,
3439

3540
/// The name of the database.
36-
#[partial(bpaf(env("PGDATABASE"), long("database")))]
41+
/// Can also be set via the `PGDATABASE` environment variable.
42+
#[partial(bpaf(long("database")))]
3743
pub database: String,
3844

3945
#[partial(bpaf(long("allow_statement_executions_against")))]

0 commit comments

Comments
 (0)