feat: implement net.cidr_is_valid builtin (#422)

Signed-off-by: tjons <tylerschade99@gmail.com>
This commit is contained in:
Tyler Schade
2025-08-06 06:17:10 -04:00
committed by GitHub
parent c5b2b0df97
commit a8384da070
4 changed files with 95 additions and 0 deletions

View File

@@ -32,6 +32,7 @@ http = []
glob = ["dep:globset"]
graph = []
jsonschema = ["dep:jsonschema"]
net = []
no_std = ["lazy_static/spin_no_std"]
opa-runtime = []
regex = ["dep:regex"]
@@ -50,6 +51,7 @@ full-opa = [
"hex",
"http",
"jsonschema",
"net",
"opa-runtime",
"regex",
"semver",

View File

@@ -14,6 +14,9 @@ mod glob;
mod graph;
#[cfg(feature = "http")]
mod http;
#[cfg(feature = "net")]
mod net;
pub mod numbers;
mod objects;
#[cfg(feature = "opa-runtime")]
@@ -80,6 +83,8 @@ lazy_static! {
//graphql::register(&mut m);
#[cfg(feature = "http")]
http::register(&mut m);
#[cfg(feature = "net")]
net::register(&mut m);
//net::register(&mut m);
#[cfg(feature = "uuid")]
uuid::register(&mut m);

86
src/builtins/net.rs Normal file
View File

@@ -0,0 +1,86 @@
use core::net::IpAddr;
use std::sync::Arc;
use crate::ast::{Expr, Ref};
use crate::builtins;
use crate::builtins::utils::ensure_args_count;
use crate::lexer::Span;
use crate::value::Value;
use anyhow::Result;
use super::utils::ensure_string;
pub fn register(m: &mut builtins::BuiltinsMap<&'static str, builtins::BuiltinFcn>) {
m.insert("net.cidr_is_valid", (cidr_is_valid, 1));
}
/// Checks if a CIDR string is valid or invalid. Uses the
/// `net::IpAddr` type to determine if the string is a valid IP,
/// and checks to ensure that the mask is in bounds for the parsed
/// IP address type (v4 or v6).
pub fn cidr_is_valid(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
ensure_args_count(span, "cidr_is_valid", params, args, 1)?;
let cidr = ensure_string("cidr_is_valid", &params[0], &args[0])?;
Ok(Value::from(is_valid_cidr(cidr)))
}
fn is_valid_cidr(cidr: Arc<str>) -> bool {
let Some((ip_addr, prefix_len)) = cidr.split_once("/") else {
return false;
};
match ip_addr.parse::<IpAddr>() {
Ok(addr) => {
let Ok(mask) = prefix_len.parse::<i16>() else {
return false;
};
match addr {
IpAddr::V4(_) => {
if !(0..=32).contains(&mask) {
return false;
}
}
IpAddr::V6(_) => {
if !(0..=128).contains(&mask) {
return false;
}
}
}
true
}
Err(_) => false,
}
}
#[cfg(test)]
mod net_tests {
use super::*;
use std::vec::Vec;
#[test]
fn test_cidr_is_valid() {
let valids = Vec::from(["127.0.0.1/32", "10.0.0.0/8", "0.1.2.3/32", "::1/128"]);
let invalids = Vec::from(["256.0.0.0/8", "127.0.0.1/33", "::1/129"]);
for cidr in valids {
assert!(
is_valid_cidr(Arc::from(cidr)),
"Valid CIDR {cidr} deemed invalid"
);
}
for cidr in invalids {
assert!(
!is_valid_cidr(Arc::from(cidr)),
"Invalid CIDR {cidr} deemed valid"
);
}
}
}

View File

@@ -45,6 +45,7 @@ v0/jsonremoveidempotent
v0/jsonschema
v0/negation
v0/nestedreferences
v0/netcidrisvalid
v0/numbersrange
v0/numbersrangestep
v0/objectfilter
@@ -149,6 +150,7 @@ v1/jsonremoveidempotent
v1/jsonschema
v1/negation
v1/nestedreferences
v1/netcidrisvalid
v1/numbersrange
v1/numbersrangestep
v1/objectfilter