Skip to content

Commit a8384da

Browse files
authored
feat: implement net.cidr_is_valid builtin (microsoft#422)
Signed-off-by: tjons <tylerschade99@gmail.com>
1 parent c5b2b0d commit a8384da

4 files changed

Lines changed: 95 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ http = []
3232
glob = ["dep:globset"]
3333
graph = []
3434
jsonschema = ["dep:jsonschema"]
35+
net = []
3536
no_std = ["lazy_static/spin_no_std"]
3637
opa-runtime = []
3738
regex = ["dep:regex"]
@@ -50,6 +51,7 @@ full-opa = [
5051
"hex",
5152
"http",
5253
"jsonschema",
54+
"net",
5355
"opa-runtime",
5456
"regex",
5557
"semver",

src/builtins/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ mod glob;
1414
mod graph;
1515
#[cfg(feature = "http")]
1616
mod http;
17+
#[cfg(feature = "net")]
18+
mod net;
19+
1720
pub mod numbers;
1821
mod objects;
1922
#[cfg(feature = "opa-runtime")]
@@ -80,6 +83,8 @@ lazy_static! {
8083
//graphql::register(&mut m);
8184
#[cfg(feature = "http")]
8285
http::register(&mut m);
86+
#[cfg(feature = "net")]
87+
net::register(&mut m);
8388
//net::register(&mut m);
8489
#[cfg(feature = "uuid")]
8590
uuid::register(&mut m);

src/builtins/net.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use core::net::IpAddr;
2+
use std::sync::Arc;
3+
4+
use crate::ast::{Expr, Ref};
5+
use crate::builtins;
6+
use crate::builtins::utils::ensure_args_count;
7+
use crate::lexer::Span;
8+
use crate::value::Value;
9+
10+
use anyhow::Result;
11+
12+
use super::utils::ensure_string;
13+
14+
pub fn register(m: &mut builtins::BuiltinsMap<&'static str, builtins::BuiltinFcn>) {
15+
m.insert("net.cidr_is_valid", (cidr_is_valid, 1));
16+
}
17+
18+
/// Checks if a CIDR string is valid or invalid. Uses the
19+
/// `net::IpAddr` type to determine if the string is a valid IP,
20+
/// and checks to ensure that the mask is in bounds for the parsed
21+
/// IP address type (v4 or v6).
22+
pub fn cidr_is_valid(
23+
span: &Span,
24+
params: &[Ref<Expr>],
25+
args: &[Value],
26+
_strict: bool,
27+
) -> Result<Value> {
28+
ensure_args_count(span, "cidr_is_valid", params, args, 1)?;
29+
let cidr = ensure_string("cidr_is_valid", &params[0], &args[0])?;
30+
31+
Ok(Value::from(is_valid_cidr(cidr)))
32+
}
33+
34+
fn is_valid_cidr(cidr: Arc<str>) -> bool {
35+
let Some((ip_addr, prefix_len)) = cidr.split_once("/") else {
36+
return false;
37+
};
38+
match ip_addr.parse::<IpAddr>() {
39+
Ok(addr) => {
40+
let Ok(mask) = prefix_len.parse::<i16>() else {
41+
return false;
42+
};
43+
44+
match addr {
45+
IpAddr::V4(_) => {
46+
if !(0..=32).contains(&mask) {
47+
return false;
48+
}
49+
}
50+
IpAddr::V6(_) => {
51+
if !(0..=128).contains(&mask) {
52+
return false;
53+
}
54+
}
55+
}
56+
true
57+
}
58+
Err(_) => false,
59+
}
60+
}
61+
62+
#[cfg(test)]
63+
mod net_tests {
64+
use super::*;
65+
use std::vec::Vec;
66+
67+
#[test]
68+
fn test_cidr_is_valid() {
69+
let valids = Vec::from(["127.0.0.1/32", "10.0.0.0/8", "0.1.2.3/32", "::1/128"]);
70+
let invalids = Vec::from(["256.0.0.0/8", "127.0.0.1/33", "::1/129"]);
71+
72+
for cidr in valids {
73+
assert!(
74+
is_valid_cidr(Arc::from(cidr)),
75+
"Valid CIDR {cidr} deemed invalid"
76+
);
77+
}
78+
79+
for cidr in invalids {
80+
assert!(
81+
!is_valid_cidr(Arc::from(cidr)),
82+
"Invalid CIDR {cidr} deemed valid"
83+
);
84+
}
85+
}
86+
}

tests/opa.passing

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ v0/jsonremoveidempotent
4545
v0/jsonschema
4646
v0/negation
4747
v0/nestedreferences
48+
v0/netcidrisvalid
4849
v0/numbersrange
4950
v0/numbersrangestep
5051
v0/objectfilter
@@ -149,6 +150,7 @@ v1/jsonremoveidempotent
149150
v1/jsonschema
150151
v1/negation
151152
v1/nestedreferences
153+
v1/netcidrisvalid
152154
v1/numbersrange
153155
v1/numbersrangestep
154156
v1/objectfilter

0 commit comments

Comments
 (0)