jlcodes99/cockpit-tools · info

email regex should be valid

Error message

email regex should be valid

What it means

The logger builds a static LazyLock<Regex> used to redact email addresses from log output, calling .expect("email regex should be valid"). Since the pattern is a compile-time constant that is known-valid, this panic can only fire if the regex source in the code is edited into an invalid form — a developer/build-time defect, not a runtime input problem.

Source

Thrown at crates/cockpit-core/src/modules/logger.rs:20

use chrono::{DateTime, Duration, Local};
use regex::{Captures, Regex};
use std::fs;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use tracing::{error, info, warn};
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};

const LOG_FILE_PREFIX: &str = "app.log";
const LOG_RETENTION_DAYS: i64 = 3;
const DEFAULT_LOG_TAIL_LINES: usize = 200;
const MIN_LOG_TAIL_LINES: usize = 20;
const MAX_LOG_TAIL_LINES: usize = 5000;
const LOG_TAIL_SCAN_CHUNK_BYTES: usize = 8192;
static EMAIL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r"(?i)\b[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}\b")
        .expect("email regex should be valid")
});

struct LocalTimer;

impl tracing_subscriber::fmt::time::FormatTime for LocalTimer {
    fn format_time(&self, w: &mut tracing_subscriber::fmt::format::Writer<'_>) -> std::fmt::Result {
        let now = chrono::Local::now();
        write!(w, "{}", now.to_rfc3339())
    }
}

pub fn get_log_dir() -> Result<PathBuf, String> {
    let data_dir = get_data_dir()?;
    let log_dir = data_dir.join("logs");

    if !log_dir.exists() {
        fs::create_dir_all(&log_dir).map_err(|e| format!("创建日志目录失败: {}", e))?;
    }

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Validate the pattern with regex::Regex::new in a unit test or quick check before building.
  2. Fix the regex syntax; remove PCRE-only constructs (lookarounds, backreferences) unsupported by the regex crate.
  3. If pattern validity must be dynamic, replace .expect with graceful error handling returning a no-redaction fallback.
  4. Pin/align the regex crate version if a feature used by the pattern disappeared.

Example fix

// before
Regex::new(r"(?i)\b[a-z0-9._%+\-]+(?=@)[a-z0-9.@]+\b")
    .expect("email regex should be valid") // lookahead: unsupported -> panic

// after
Regex::new(r"(?i)\b[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}\b")
    .expect("email regex should be valid")
Defensive patterns

Strategy: validation

Validate before calling

#[cfg(test)]
#[test]
fn email_regex_is_valid() {
    Regex::new(r"(?i)\b[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}\b")
        .expect("email regex should be valid");
}

Try / catch

static EMAIL_REGEX: LazyLock<Option<Regex>> = LazyLock::new(|| {
    Regex::new(r"(?i)\b[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}\b").ok()
}); // redact only when Some

Prevention

When it happens

Trigger: Logger initialization when the hardcoded regex literal is invalid — i.e. only after someone edits the EMAIL_REGEX pattern with a syntax error, bad escape, or an unsupported feature for the installed regex crate version.

Common situations: Hand-editing the redaction pattern (e.g. adding a lookahead, which the regex crate does not support); upgrading/downgrading the regex crate across feature changes; copy-pasting a PCRE-style pattern into the Rust regex crate.

Related errors


AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05). Data as JSON: /api/errors/dafeba068c247879. Report an issue: GitHub.