hive-priv: redact password-bearing lines before logging forgejo admin output
This commit is contained in:
parent
c149963917
commit
7a3614552b
1 changed files with 34 additions and 3 deletions
|
|
@ -1366,6 +1366,21 @@ fn validate_forge_admin_arg(arg: &str) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Redact a line before it hits the (root-readable, but still
|
||||
/// unnecessarily exposed) host journal. `forgejo admin user create
|
||||
/// --random-password` prints the generated password straight to stdout —
|
||||
/// case-insensitive substring match on "password" is deliberately broad
|
||||
/// (not pinned to forgejo's exact wording, which can change across
|
||||
/// versions) so any password-bearing line gets caught rather than relying
|
||||
/// on a phrase that could silently drift out of sync.
|
||||
fn redact_password_line(line: &str) -> std::borrow::Cow<'_, str> {
|
||||
if line.to_ascii_lowercase().contains("password") {
|
||||
std::borrow::Cow::Borrowed("[redacted: line mentions a password]")
|
||||
} else {
|
||||
std::borrow::Cow::Borrowed(line)
|
||||
}
|
||||
}
|
||||
|
||||
/// Run `forgejo admin <args>` inside the `hive-forge` container as the
|
||||
/// `forgejo` unix user. Requires root (for nsenter into the container's
|
||||
/// namespaces). Returns `(stdout, stderr)`.
|
||||
|
|
@ -1394,10 +1409,10 @@ async fn run_forge_admin(args: &[String]) -> Result<(String, String)> {
|
|||
let stdout = String::from_utf8_lossy(&out.stdout).into_owned();
|
||||
let stderr = String::from_utf8_lossy(&out.stderr).into_owned();
|
||||
for line in stdout.lines() {
|
||||
tracing::info!(target: "forgejo-admin", "{line}");
|
||||
tracing::info!(target: "forgejo-admin", "{}", redact_password_line(line));
|
||||
}
|
||||
for line in stderr.lines() {
|
||||
tracing::warn!(target: "forgejo-admin", "{line}");
|
||||
tracing::warn!(target: "forgejo-admin", "{}", redact_password_line(line));
|
||||
}
|
||||
if !out.status.success() {
|
||||
bail!(
|
||||
|
|
@ -1986,10 +2001,26 @@ async fn sync_agent_tmpfiles(agents: &[String]) -> Result<(String, String)> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::write_state_file_nofollow;
|
||||
use super::{redact_password_line, write_state_file_nofollow};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
#[test]
|
||||
fn redacts_lines_mentioning_password_case_insensitively() {
|
||||
assert_eq!(
|
||||
redact_password_line("New password: hunter2"),
|
||||
"[redacted: line mentions a password]"
|
||||
);
|
||||
assert_eq!(
|
||||
redact_password_line("PASSWORD=hunter2"),
|
||||
"[redacted: line mentions a password]"
|
||||
);
|
||||
assert_eq!(
|
||||
redact_password_line("User \"foo\" was successfully created."),
|
||||
"User \"foo\" was successfully created."
|
||||
);
|
||||
}
|
||||
|
||||
/// Unique scratch dir per test, no external tempfile dep.
|
||||
fn scratch() -> PathBuf {
|
||||
static CTR: AtomicU32 = AtomicU32::new(0);
|
||||
|
|
|
|||
Loading…
Reference in a new issue