use crate::config::DjogiConfig;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OutOfOrderPolicy {
AllowWithDiagnostic,
Reject,
AllowExplicit {
override_reason: String,
},
}
impl OutOfOrderPolicy {
pub fn default_for_config(config: &DjogiConfig) -> Self {
if config.is_production() || ci_env_set() {
OutOfOrderPolicy::Reject
} else {
OutOfOrderPolicy::AllowWithDiagnostic
}
}
pub fn allows(&self) -> bool {
match self {
OutOfOrderPolicy::AllowWithDiagnostic => true,
OutOfOrderPolicy::AllowExplicit { .. } => true,
OutOfOrderPolicy::Reject => false,
}
}
pub fn override_reason(&self) -> Option<&str> {
match self {
OutOfOrderPolicy::AllowExplicit { override_reason } => Some(override_reason.as_str()),
_ => None,
}
}
}
fn ci_env_set() -> bool {
match std::env::var("CI") {
Ok(v) => ascii_eq_ignore_case(v.as_bytes(), b"true"),
Err(_) => false,
}
}
pub(crate) fn ascii_eq_ignore_case(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
for (x, y) in a.iter().zip(b.iter()) {
if !x.eq_ignore_ascii_case(y) {
return false;
}
}
true
}
const LOCALHOST_ALLOWLIST: &[&str] = &["", "127.0.0.1", "::1", "localhost"];
pub fn is_localhost_connection(conn: &str) -> bool {
let host = extract_host(conn);
if LOCALHOST_ALLOWLIST.binary_search(&host).is_ok() {
return true;
}
is_ipv4_loopback_range(host)
}
fn is_ipv4_loopback_range(host: &str) -> bool {
let bytes = host.as_bytes();
let mut octets = [0u16; 4];
let mut octet_idx = 0usize;
let mut acc: u16 = 0;
let mut digits_in_octet: u8 = 0;
for &b in bytes {
if b == b'.' {
if digits_in_octet == 0 || octet_idx >= 3 {
return false;
}
octets[octet_idx] = acc;
octet_idx += 1;
acc = 0;
digits_in_octet = 0;
continue;
}
if !b.is_ascii_digit() {
return false;
}
if digits_in_octet >= 3 {
return false;
}
acc = acc * 10 + (b - b'0') as u16;
if acc > 255 {
return false;
}
digits_in_octet += 1;
}
if octet_idx != 3 || digits_in_octet == 0 {
return false;
}
octets[3] = acc;
octets[0] == 127
}
fn extract_host(conn: &str) -> &str {
let trimmed = conn.trim();
if trimmed.is_empty() {
return "";
}
if let Some(rest) = strip_scheme(trimmed) {
return extract_url_host(rest);
}
extract_libpq_host(trimmed)
}
fn strip_scheme(s: &str) -> Option<&str> {
if let Some(rest) = s.strip_prefix("postgres://") {
return Some(rest);
}
if let Some(rest) = s.strip_prefix("postgresql://") {
return Some(rest);
}
None
}
fn extract_url_host(body: &str) -> &str {
let bytes = body.as_bytes();
let mut authority_end = bytes.len();
for (i, &b) in bytes.iter().enumerate() {
if b == b'/' || b == b'?' {
authority_end = i;
break;
}
}
let authority = &body[..authority_end];
let host_port = match authority.rfind('@') {
Some(idx) => &authority[idx + 1..],
None => authority,
};
if let Some(rest) = host_port.strip_prefix('[')
&& let Some(end) = rest.find(']')
{
return &rest[..end];
}
match host_port.find(':') {
Some(idx) => &host_port[..idx],
None => host_port,
}
}
fn extract_libpq_host(s: &str) -> &str {
let bytes = s.as_bytes();
let mut last_host_start: Option<usize> = None;
let mut last_host_end: usize = 0;
let mut i = 0usize;
while i < bytes.len() {
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
if i >= bytes.len() {
break;
}
let key_start = i;
while i < bytes.len() && bytes[i] != b'=' && !bytes[i].is_ascii_whitespace() {
i += 1;
}
let key_end = i;
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
if i >= bytes.len() || bytes[i] != b'=' {
continue;
}
i += 1; while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
if i < bytes.len() && (bytes[i] == b'\'' || bytes[i] == b'"') {
let quote = bytes[i];
i += 1; let inner_start = i;
while i < bytes.len() {
if bytes[i] == b'\\' && i + 1 < bytes.len() {
i += 2;
continue;
}
if bytes[i] == quote {
break;
}
i += 1;
}
let inner_end = i;
if i < bytes.len() && bytes[i] == quote {
i += 1;
}
if matches_key(&bytes[key_start..key_end], b"host") {
last_host_start = Some(inner_start);
last_host_end = inner_end;
}
continue;
}
let value_start = i;
while i < bytes.len() && !bytes[i].is_ascii_whitespace() {
i += 1;
}
let value_end = i;
if matches_key(&bytes[key_start..key_end], b"host") {
last_host_start = Some(value_start);
last_host_end = value_end;
}
}
match last_host_start {
Some(start) => &s[start..last_host_end],
None => "",
}
}
fn matches_key(key: &[u8], target: &[u8]) -> bool {
key == target
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::DjogiConfig;
fn cfg_with_profile(profile: &str) -> DjogiConfig {
DjogiConfig {
profile: profile.to_string(),
..DjogiConfig::default()
}
}
#[test]
fn default_for_config_dev_profile_allows() {
let prior = std::env::var("CI").ok();
unsafe {
std::env::remove_var("CI");
}
let cfg = cfg_with_profile("development");
let policy = OutOfOrderPolicy::default_for_config(&cfg);
assert_eq!(policy, OutOfOrderPolicy::AllowWithDiagnostic);
if let Some(v) = prior {
unsafe {
std::env::set_var("CI", v);
}
}
}
#[test]
fn default_for_config_production_profile_rejects() {
let prior = std::env::var("CI").ok();
unsafe {
std::env::remove_var("CI");
}
let cfg = cfg_with_profile("production");
let policy = OutOfOrderPolicy::default_for_config(&cfg);
assert_eq!(policy, OutOfOrderPolicy::Reject);
if let Some(v) = prior {
unsafe {
std::env::set_var("CI", v);
}
}
}
#[test]
fn default_for_config_ci_env_rejects_even_in_dev() {
let prior = std::env::var("CI").ok();
unsafe {
std::env::set_var("CI", "true");
}
let cfg = cfg_with_profile("development");
let policy = OutOfOrderPolicy::default_for_config(&cfg);
assert_eq!(policy, OutOfOrderPolicy::Reject);
match prior {
Some(v) => unsafe { std::env::set_var("CI", v) },
None => unsafe { std::env::remove_var("CI") },
}
}
#[test]
fn default_for_config_ci_uppercase_also_rejects() {
let prior = std::env::var("CI").ok();
unsafe {
std::env::set_var("CI", "TRUE");
}
let cfg = cfg_with_profile("development");
let policy = OutOfOrderPolicy::default_for_config(&cfg);
assert_eq!(policy, OutOfOrderPolicy::Reject);
match prior {
Some(v) => unsafe { std::env::set_var("CI", v) },
None => unsafe { std::env::remove_var("CI") },
}
}
#[test]
fn default_for_config_ci_arbitrary_string_does_not_reject() {
let prior = std::env::var("CI").ok();
unsafe {
std::env::set_var("CI", "1");
}
let cfg = cfg_with_profile("development");
let policy = OutOfOrderPolicy::default_for_config(&cfg);
assert_eq!(policy, OutOfOrderPolicy::AllowWithDiagnostic);
match prior {
Some(v) => unsafe { std::env::set_var("CI", v) },
None => unsafe { std::env::remove_var("CI") },
}
}
#[test]
fn allows_returns_true_for_allow_variants() {
assert!(OutOfOrderPolicy::AllowWithDiagnostic.allows());
assert!(
OutOfOrderPolicy::AllowExplicit {
override_reason: "cherry-pick from main".to_string(),
}
.allows()
);
}
#[test]
fn allows_returns_false_for_reject() {
assert!(!OutOfOrderPolicy::Reject.allows());
}
#[test]
fn override_reason_returned_only_for_allow_explicit() {
assert_eq!(
OutOfOrderPolicy::AllowWithDiagnostic.override_reason(),
None
);
assert_eq!(OutOfOrderPolicy::Reject.override_reason(), None);
let p = OutOfOrderPolicy::AllowExplicit {
override_reason: "documented reason".to_string(),
};
assert_eq!(p.override_reason(), Some("documented reason"));
}
#[test]
fn ascii_eq_ignore_case_basic() {
assert!(ascii_eq_ignore_case(b"true", b"true"));
assert!(ascii_eq_ignore_case(b"True", b"true"));
assert!(ascii_eq_ignore_case(b"TRUE", b"true"));
assert!(!ascii_eq_ignore_case(b"truth", b"true"));
assert!(!ascii_eq_ignore_case(b"", b"true"));
assert!(ascii_eq_ignore_case(b"", b""));
}
#[test]
fn extract_host_url_simple() {
assert_eq!(extract_host("postgres://localhost/db"), "localhost");
assert_eq!(extract_host("postgres://localhost:5432/db"), "localhost");
}
#[test]
fn extract_host_url_with_userinfo() {
assert_eq!(
extract_host("postgres://user:pass@localhost:5432/db"),
"localhost"
);
assert_eq!(extract_host("postgres://user@localhost/db"), "localhost");
}
#[test]
fn extract_host_url_postgresql_alias() {
assert_eq!(extract_host("postgresql://localhost/db"), "localhost");
}
#[test]
fn extract_host_url_no_path() {
assert_eq!(extract_host("postgres://localhost"), "localhost");
assert_eq!(extract_host("postgres://localhost:5432"), "localhost");
}
#[test]
fn extract_host_url_127_0_0_1() {
assert_eq!(extract_host("postgres://127.0.0.1:5432/db"), "127.0.0.1");
}
#[test]
fn extract_host_url_remote_host() {
assert_eq!(
extract_host("postgres://db.prod.example.com:5432/main"),
"db.prod.example.com"
);
}
#[test]
fn extract_host_url_ipv6_bracketed() {
assert_eq!(extract_host("postgres://[::1]:5432/db"), "::1");
assert_eq!(extract_host("postgres://user@[::1]:5432/db"), "::1");
assert_eq!(extract_host("postgres://[::1]/db"), "::1");
}
#[test]
fn extract_host_url_with_query_params() {
assert_eq!(
extract_host("postgres://localhost?sslmode=disable"),
"localhost"
);
}
#[test]
fn extract_host_libpq_basic() {
assert_eq!(extract_host("host=localhost dbname=test"), "localhost");
}
#[test]
fn extract_host_libpq_no_host_param() {
assert_eq!(extract_host("dbname=test user=postgres"), "");
}
#[test]
fn extract_host_libpq_with_quotes() {
assert_eq!(extract_host("host='localhost' dbname=test"), "localhost");
}
#[test]
fn extract_host_libpq_last_wins() {
assert_eq!(
extract_host("host=remote.example.com host=127.0.0.1"),
"127.0.0.1"
);
}
#[test]
fn extract_host_libpq_empty_string() {
assert_eq!(extract_host(""), "");
}
#[test]
fn extract_host_libpq_remote() {
assert_eq!(
extract_host("host=db.prod.example.com port=5432"),
"db.prod.example.com"
);
}
#[test]
fn is_localhost_connection_url_localhost() {
assert!(is_localhost_connection("postgres://localhost/test"));
assert!(is_localhost_connection("postgres://localhost:5432/test"));
assert!(is_localhost_connection(
"postgres://user:pass@localhost:5432/test"
));
}
#[test]
fn is_localhost_connection_url_127_0_0_1() {
assert!(is_localhost_connection("postgres://127.0.0.1:5432/test"));
assert!(is_localhost_connection("postgresql://127.0.0.1/test"));
}
#[test]
fn is_localhost_connection_url_ipv6() {
assert!(is_localhost_connection("postgres://[::1]:5432/test"));
}
#[test]
fn is_localhost_connection_url_remote_rejected() {
assert!(!is_localhost_connection(
"postgres://db.prod.example.com:5432/main"
));
assert!(!is_localhost_connection("postgres://10.0.0.5/test"));
assert!(!is_localhost_connection("postgres://localhostt/test"));
}
#[test]
fn is_localhost_connection_libpq_localhost() {
assert!(is_localhost_connection("host=localhost dbname=test"));
assert!(is_localhost_connection("host=127.0.0.1 dbname=test"));
assert!(is_localhost_connection("host=::1 dbname=test"));
}
#[test]
fn is_localhost_connection_libpq_no_host_param() {
assert!(is_localhost_connection("dbname=test"));
assert!(is_localhost_connection(""));
assert!(is_localhost_connection(" "));
}
#[test]
fn is_localhost_connection_libpq_remote_rejected() {
assert!(!is_localhost_connection(
"host=db.prod.example.com dbname=test"
));
assert!(!is_localhost_connection("host=10.0.0.5"));
}
#[test]
fn is_localhost_connection_libpq_quoted_localhost() {
assert!(is_localhost_connection("host='localhost' dbname=test"));
}
#[test]
fn extract_host_libpq_padded_equals_single_space_each_side() {
assert_eq!(extract_host("host = prod dbname=test"), "prod");
}
#[test]
fn extract_host_libpq_padded_equals_double_space_each_side() {
assert_eq!(extract_host("host = prod dbname=test"), "prod");
}
#[test]
fn extract_host_libpq_padded_equals_only_after() {
assert_eq!(extract_host("host= prod dbname=test"), "prod");
}
#[test]
fn extract_host_libpq_padded_equals_only_before() {
assert_eq!(extract_host("host =prod dbname=test"), "prod");
}
#[test]
fn extract_host_libpq_padded_equals_quoted_value() {
assert_eq!(
extract_host("host = 'prod with space' dbname=test"),
"prod with space"
);
}
#[test]
fn extract_host_libpq_padded_equals_remote_hostname() {
assert_eq!(
extract_host("host = prod.example.com dbname=main"),
"prod.example.com"
);
assert!(!is_localhost_connection(
"host = prod.example.com dbname=main"
));
}
#[test]
fn is_localhost_connection_libpq_padded_equals_remote_rejected() {
assert!(!is_localhost_connection(
"host = db.prod.example.com dbname=test"
));
assert!(!is_localhost_connection("host = 10.0.0.5 dbname=test"));
assert!(!is_localhost_connection("host= prod dbname=test"));
assert!(!is_localhost_connection("host =prod dbname=test"));
}
#[test]
fn is_localhost_connection_libpq_padded_equals_localhost_still_passes() {
assert!(is_localhost_connection("host = localhost dbname=test"));
assert!(is_localhost_connection("host = 127.0.0.1 dbname=test"));
assert!(is_localhost_connection("host= ::1 dbname=test"));
}
#[test]
fn extract_host_libpq_double_quoted_value() {
assert_eq!(extract_host("host=\"localhost\" dbname=test"), "localhost");
}
#[test]
fn extract_host_libpq_double_quoted_with_space() {
assert_eq!(
extract_host("host=\"prod with space\" dbname=test"),
"prod with space"
);
}
#[test]
fn extract_host_libpq_mixed_quotes() {
assert_eq!(extract_host("host=\"x'y\" dbname=test"), "x'y");
assert_eq!(extract_host("host='x\"y' dbname=test"), "x\"y");
}
#[test]
fn is_localhost_connection_libpq_double_quoted_localhost() {
assert!(is_localhost_connection("host=\"localhost\" dbname=test"));
assert!(is_localhost_connection("host=\"127.0.0.1\" dbname=test"));
assert!(!is_localhost_connection(
"host=\"db.prod.example.com\" dbname=test"
));
}
#[test]
fn extract_host_libpq_double_quoted_with_escaped_quote() {
assert_eq!(
extract_host("host=\"foo\\\"bar\" dbname=test"),
"foo\\\"bar"
);
assert_eq!(extract_host("host='foo\\'bar' dbname=test"), "foo\\'bar");
assert!(!is_localhost_connection("host=\"foo\\\"bar\" dbname=test"));
assert!(!is_localhost_connection("host='foo\\'bar' dbname=test"));
}
#[test]
fn extract_host_libpq_empty_value_consumes_next_token() {
assert_eq!(extract_host("host= dbname=test"), "dbname=test");
assert!(!is_localhost_connection("host= dbname=test"));
}
#[test]
fn u_partial_is_ipv4_loopback_range_accepts_127_dot_x_y_z() {
assert!(is_ipv4_loopback_range("127.0.0.1"));
assert!(is_ipv4_loopback_range("127.0.0.0"));
assert!(is_ipv4_loopback_range("127.5.10.20"));
assert!(is_ipv4_loopback_range("127.255.255.254"));
assert!(is_ipv4_loopback_range("127.255.255.255"));
assert!(is_ipv4_loopback_range("127.1.1.1"));
}
#[test]
fn u_partial_is_ipv4_loopback_range_rejects_non_127_addresses() {
assert!(!is_ipv4_loopback_range("128.0.0.1"));
assert!(!is_ipv4_loopback_range("10.0.0.1"));
assert!(!is_ipv4_loopback_range("192.168.1.1"));
assert!(!is_ipv4_loopback_range("0.0.0.0"));
assert!(!is_ipv4_loopback_range("126.255.255.255"));
assert!(!is_ipv4_loopback_range("255.255.255.255"));
}
#[test]
fn u_partial_is_ipv4_loopback_range_rejects_malformed_inputs() {
assert!(!is_ipv4_loopback_range(""));
assert!(!is_ipv4_loopback_range("127"));
assert!(!is_ipv4_loopback_range("127.0"));
assert!(!is_ipv4_loopback_range("127.0.0"));
assert!(!is_ipv4_loopback_range("127.0.0.1.5")); assert!(!is_ipv4_loopback_range("127.0.0."));
assert!(!is_ipv4_loopback_range(".127.0.0.1"));
assert!(!is_ipv4_loopback_range("127..0.1"));
assert!(!is_ipv4_loopback_range("127.0.0.256")); assert!(!is_ipv4_loopback_range("127.0.0.999"));
assert!(!is_ipv4_loopback_range("127.a.0.1")); assert!(!is_ipv4_loopback_range("127.0.0.0001")); assert!(!is_ipv4_loopback_range("localhost")); assert!(!is_ipv4_loopback_range("::1"));
}
#[test]
fn u_partial_is_localhost_connection_recognises_full_127_range() {
assert!(is_localhost_connection("postgres://127.5.10.20:5432/test"));
assert!(is_localhost_connection("postgres://127.0.42.1/test"));
assert!(is_localhost_connection("postgres://127.255.255.254/test"));
assert!(is_localhost_connection("host=127.5.10.20 dbname=test"));
assert!(is_localhost_connection("host='127.0.42.1' dbname=test"));
assert!(!is_localhost_connection("postgres://10.0.0.5/test"));
assert!(!is_localhost_connection("host=128.0.0.1 dbname=test"));
}
}