fix: greedy secrets regex caused multiple secrets on one line to fail
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@ use std::sync::{Arc, LazyLock};
|
|||||||
use tokio::runtime::Handle;
|
use tokio::runtime::Handle;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub static SECRET_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\{\{(.+)}}").unwrap());
|
pub static SECRET_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\{\{([^{}]+)}}").unwrap());
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Vault {
|
pub struct Vault {
|
||||||
|
|||||||
+39
-18
@@ -537,9 +537,48 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn interpolates_multiple_secrets_on_same_line() {
|
||||||
|
let calls = Calls::new();
|
||||||
|
|
||||||
|
let (out, missing) = interpolate_secrets_with("url={{URL}} key={{KEY}}", None, |name| {
|
||||||
|
calls.record(name);
|
||||||
|
match name {
|
||||||
|
"URL" => Ok("https://example.test".to_string()),
|
||||||
|
"KEY" => Ok("sk-12345".to_string()),
|
||||||
|
other => panic!("unexpected lookup: {other}"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(calls.snapshot(), vec!["URL".to_string(), "KEY".to_string()]);
|
||||||
|
assert_eq!(out, "url=https://example.test key=sk-12345");
|
||||||
|
assert!(missing.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regex_rejects_braces_in_secret_names() {
|
||||||
|
let calls = Calls::new();
|
||||||
|
|
||||||
|
let (out, missing) =
|
||||||
|
interpolate_secrets_with("literal {{ {NOT_A_NAME} }} text", None, |name| {
|
||||||
|
calls.record(name);
|
||||||
|
Ok(format!("got-{name}"))
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
calls.snapshot().is_empty(),
|
||||||
|
"name with embedded braces must not match"
|
||||||
|
);
|
||||||
|
assert_eq!(out, "literal {{ {NOT_A_NAME} }} text");
|
||||||
|
assert!(missing.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fatal_failure_short_circuits_remaining_lines() {
|
fn fatal_failure_short_circuits_remaining_lines() {
|
||||||
let calls = Calls::new();
|
let calls = Calls::new();
|
||||||
|
|
||||||
let result =
|
let result =
|
||||||
interpolate_secrets_with("a={{S1}}\nb={{S2}}\nc={{S3}}\nd={{S4}}", None, |name| {
|
interpolate_secrets_with("a={{S1}}\nb={{S2}}\nc={{S3}}\nd={{S4}}", None, |name| {
|
||||||
calls.record(name);
|
calls.record(name);
|
||||||
@@ -578,22 +617,4 @@ mod tests {
|
|||||||
"expected hint contents, got: {err}"
|
"expected hint contents, got: {err}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn regex_greedy_capture_collapses_multi_secret_line_into_single_lookup() {
|
|
||||||
let calls = Calls::new();
|
|
||||||
let (out, missing) = interpolate_secrets_with("url={{URL}} key={{KEY}}", None, |name| {
|
|
||||||
calls.record(name);
|
|
||||||
Err(not_found(name))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
calls.snapshot(),
|
|
||||||
vec!["URL}} key={{KEY".to_string()],
|
|
||||||
"greedy regex spans first {{ to last }}, collapsing the whole line into one bogus lookup"
|
|
||||||
);
|
|
||||||
assert_eq!(out, "url=");
|
|
||||||
assert_eq!(missing, vec!["URL}} key={{KEY".to_string()]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user