mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
crypto builtins (#57)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
ab968c2386
commit
70bf371ebf
32
Cargo.toml
32
Cargo.toml
@@ -5,26 +5,47 @@ edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["full-opa"]
|
||||
|
||||
crypto = ["dep:constant_time_eq", "dep:hmac", "dep:hex", "dep:md-5", "dep:sha1", "dep:sha2"]
|
||||
deprecated = []
|
||||
glob = ["dep:wax"]
|
||||
regex = ["dep:regex"]
|
||||
semver = ["dep:semver"]
|
||||
yaml = ["serde_yaml"]
|
||||
full-opa = ["crypto", "deprecated", "glob", "regex", "semver", "yaml"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.66"
|
||||
semver = "1.0.20"
|
||||
serde = {version = "1.0.150", features = ["derive", "rc"] }
|
||||
serde_json = {version = "1.0.89", features = ["arbitrary_precision"] }
|
||||
serde_yaml = "0.9.16"
|
||||
serde_yaml = {version = "0.9.16", optional = true }
|
||||
log = "0.4.17"
|
||||
env_logger="0.10.0"
|
||||
lazy_static = "1.4.0"
|
||||
rand = "0.8.5"
|
||||
data-encoding = "2.4.0"
|
||||
regex = "1.10.2"
|
||||
num = "0.4.1"
|
||||
rust_decimal = { version = "1.33.1", features = ["serde-with-arbitrary-precision"] }
|
||||
glob = "0.3.1"
|
||||
wax = { version = "0.6.0", features = [], default-features = false }
|
||||
|
||||
|
||||
|
||||
# Crypto
|
||||
constant_time_eq = {version = "0.3.0", optional = true}
|
||||
hmac = {version = "0.12.1", optional = true}
|
||||
sha2 = {version= "0.10.8", optional = true}
|
||||
hex = {version = "0.4.3", optional = true}
|
||||
sha1 = {version = "0.10.6", optional = true}
|
||||
md-5 = {version = "0.10.6", optional = true}
|
||||
|
||||
regex = {version = "1.10.2", optional = true}
|
||||
semver = {version = "1.0.20", optional = true}
|
||||
wax = { version = "0.6.0", features = [], default-features = false, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
clap = { version = "4.4.7", features = ["derive"] }
|
||||
serde_yaml = "0.9.16"
|
||||
test-generator = "0.3.1"
|
||||
walkdir = "2.3.2"
|
||||
|
||||
@@ -38,6 +59,7 @@ debug = true
|
||||
name="opa"
|
||||
harness=false
|
||||
test=false
|
||||
required-features = ["full-opa"]
|
||||
|
||||
[[test]]
|
||||
name="aci"
|
||||
|
||||
148
src/builtins/crypto.rs
Normal file
148
src/builtins/crypto.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::ast::{Expr, Ref};
|
||||
use crate::builtins;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_string};
|
||||
use crate::lexer::Span;
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use constant_time_eq::constant_time_eq;
|
||||
use hmac::{Hmac, Mac};
|
||||
use md5::{Digest, Md5};
|
||||
use sha1::Sha1;
|
||||
use sha2::{Sha256, Sha512};
|
||||
|
||||
pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("crypto.hmac.equal", (hmac_equal_fixed_time, 2));
|
||||
m.insert("crypto.hmac.md5", (hmac_md5, 2));
|
||||
m.insert("crypto.hmac.sha1", (hmac_sha1, 2));
|
||||
m.insert("crypto.hmac.sha256", (hmac_sha256, 2));
|
||||
m.insert("crypto.hmac.sha512", (hmac_sha512, 2));
|
||||
|
||||
m.insert("crypto.md5", (crypto_md5, 1));
|
||||
m.insert("crypto.sha1", (crypto_sha1, 1));
|
||||
m.insert("crypto.sha256", (crypto_sha256, 1));
|
||||
}
|
||||
|
||||
fn hmac_equal_fixed_time(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "crypto.hmac.equal";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let hmac1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
let hmac2 = ensure_string(name, ¶ms[1], &args[1])?;
|
||||
|
||||
Ok(Value::Bool(constant_time_eq(
|
||||
hmac1.as_bytes(),
|
||||
hmac2.as_bytes(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn hmac_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "crypto.hmac.md5";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let x = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
let key = ensure_string(name, ¶ms[1], &args[1])?;
|
||||
|
||||
let mut hmac = Hmac::<Md5>::new_from_slice(key.as_bytes())
|
||||
.or_else(|_| bail!(span.error("failed to create hmac instance")))?;
|
||||
|
||||
hmac.update(x.as_bytes());
|
||||
let result = hmac.finalize();
|
||||
|
||||
Ok(Value::String(hex::encode(result.into_bytes()).into()))
|
||||
}
|
||||
|
||||
fn hmac_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "crypto.hmac.sha1";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let x = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
let key = ensure_string(name, ¶ms[1], &args[1])?;
|
||||
|
||||
let mut hmac = Hmac::<Sha1>::new_from_slice(key.as_bytes())
|
||||
.or_else(|_| bail!(span.error("failed to create hmac instance")))?;
|
||||
|
||||
hmac.update(x.as_bytes());
|
||||
let result = hmac.finalize();
|
||||
|
||||
Ok(Value::String(hex::encode(result.into_bytes()).into()))
|
||||
}
|
||||
|
||||
fn hmac_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "crypto.hmac.sha256";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let x = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
let key = ensure_string(name, ¶ms[1], &args[1])?;
|
||||
|
||||
let mut hmac = Hmac::<Sha256>::new_from_slice(key.as_bytes())
|
||||
.or_else(|_| bail!(span.error("failed to create hmac instance")))?;
|
||||
|
||||
hmac.update(x.as_bytes());
|
||||
let result = hmac.finalize();
|
||||
|
||||
Ok(Value::String(hex::encode(result.into_bytes()).into()))
|
||||
}
|
||||
|
||||
fn hmac_sha512(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "crypto.hmac.sha512";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let x = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
let key = ensure_string(name, ¶ms[1], &args[1])?;
|
||||
|
||||
let mut hmac = Hmac::<Sha512>::new_from_slice(key.as_bytes())
|
||||
.or_else(|_| bail!(span.error("failed to create hmac instance")))?;
|
||||
|
||||
hmac.update(x.as_bytes());
|
||||
let result = hmac.finalize();
|
||||
|
||||
Ok(Value::String(hex::encode(result.into_bytes()).into()))
|
||||
}
|
||||
|
||||
fn crypto_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "crypto.md5";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
let x = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
let mut h = Md5::new();
|
||||
|
||||
h.update(x.as_bytes());
|
||||
let result = h.finalize();
|
||||
|
||||
Ok(Value::String(hex::encode(result).into()))
|
||||
}
|
||||
|
||||
fn crypto_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "crypto.sha1";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
let x = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
let mut h = Sha1::new();
|
||||
|
||||
h.update(x.as_bytes());
|
||||
let result = h.finalize();
|
||||
|
||||
Ok(Value::String(hex::encode(result).into()))
|
||||
}
|
||||
|
||||
fn crypto_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "crypto.sha256";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
let x = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
let mut h = Sha256::new();
|
||||
|
||||
h.update(x.as_bytes());
|
||||
let result = h.finalize();
|
||||
|
||||
Ok(Value::String(hex::encode(result).into()))
|
||||
}
|
||||
@@ -21,6 +21,8 @@ lazy_static! {
|
||||
m.insert("all", (all, 1));
|
||||
m.insert("any", (any, 1));
|
||||
m.insert("set_diff", (set_diff, 2));
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
m.insert("re_match", (regex_match, 2));
|
||||
m
|
||||
};
|
||||
|
||||
@@ -17,9 +17,13 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("json.is_valid", (json_is_valid, 1));
|
||||
m.insert("json.marshal", (json_marshal, 1));
|
||||
m.insert("jsonunmarshal", (json_unmarshal, 1));
|
||||
m.insert("yaml.is_valid", (yaml_is_valid, 1));
|
||||
m.insert("yaml.marshal", (yaml_marshal, 1));
|
||||
m.insert("yaml.unmarshal", (yaml_unmarshal, 1));
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
{
|
||||
m.insert("yaml.is_valid", (yaml_is_valid, 1));
|
||||
m.insert("yaml.marshal", (yaml_marshal, 1));
|
||||
m.insert("yaml.unmarshal", (yaml_unmarshal, 1));
|
||||
}
|
||||
}
|
||||
|
||||
fn base64_decode(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -33,6 +37,7 @@ fn base64_decode(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
fn yaml_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "yaml.is_valid";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
@@ -41,6 +46,7 @@ fn yaml_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
|
||||
Ok(Value::Bool(Value::from_yaml_str(&yaml_str).is_ok()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "yaml.marshal";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
@@ -51,6 +57,7 @@ fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Val
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
fn yaml_unmarshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "yaml.unmarshal";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
@@ -6,14 +6,20 @@ mod arrays;
|
||||
mod bitwise;
|
||||
pub mod comparison;
|
||||
mod conversions;
|
||||
mod debugging;
|
||||
pub mod deprecated;
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
mod crypto;
|
||||
mod debugging;
|
||||
#[cfg(feature = "deprecated")]
|
||||
pub mod deprecated;
|
||||
mod encoding;
|
||||
#[cfg(feature = "glob")]
|
||||
mod glob;
|
||||
pub mod numbers;
|
||||
mod objects;
|
||||
#[cfg(feature = "regex")]
|
||||
mod regex;
|
||||
#[cfg(feature = "semver")]
|
||||
mod semver;
|
||||
pub mod sets;
|
||||
mod strings;
|
||||
@@ -34,6 +40,7 @@ use lazy_static::lazy_static;
|
||||
|
||||
pub type BuiltinFcn = (fn(&Span, &[Ref<Expr>], &[Value]) -> Result<Value>, u8);
|
||||
|
||||
#[cfg(feature = "deprecated")]
|
||||
pub use deprecated::DEPRECATED;
|
||||
|
||||
#[rustfmt::skip]
|
||||
@@ -48,8 +55,13 @@ lazy_static! {
|
||||
sets::register(&mut m);
|
||||
objects::register(&mut m);
|
||||
strings::register(&mut m);
|
||||
|
||||
#[cfg(feature = "regex")]
|
||||
regex::register(&mut m);
|
||||
|
||||
#[cfg(feature = "glob")]
|
||||
glob::register(&mut m);
|
||||
|
||||
bitwise::register(&mut m);
|
||||
conversions::register(&mut m);
|
||||
//units::register(&mut m);
|
||||
@@ -58,12 +70,15 @@ lazy_static! {
|
||||
//token_signing::register(&mut m);
|
||||
//token_verification::register(&mut m);
|
||||
time::register(&mut m);
|
||||
//cryptography::register(&mut m);
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
crypto::register(&mut m);
|
||||
//graphs::register(&mut m);
|
||||
//graphql::register(&mut m);
|
||||
//http::register(&mut m);
|
||||
//net::register(&mut m);
|
||||
//uuid::register(&mut m);
|
||||
#[cfg(feature = "semver")]
|
||||
semver::register(&mut m);
|
||||
//rego::register(&mut m);
|
||||
//opa::register(&mut m);
|
||||
|
||||
@@ -100,7 +100,7 @@ impl Interpreter {
|
||||
module: None,
|
||||
schedule: None,
|
||||
current_module_path: String::default(),
|
||||
input: Value::new_object(),
|
||||
input: Value::Null,
|
||||
data: Value::new_object(),
|
||||
init_data: Value::new_object(),
|
||||
with_document: Value::new_object(),
|
||||
@@ -499,9 +499,35 @@ impl Interpreter {
|
||||
}
|
||||
return Ok(Value::Bool(true));
|
||||
}
|
||||
(Expr::Object { .. }, Expr::Object { .. }) => {
|
||||
// TODO: destructure
|
||||
return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs);
|
||||
(
|
||||
Expr::Object {
|
||||
fields: lhs_fields, ..
|
||||
},
|
||||
Expr::Object {
|
||||
fields: rhs_fields,
|
||||
span: rhs_span,
|
||||
},
|
||||
) => {
|
||||
if lhs_fields.len() != rhs_fields.len() {
|
||||
bail!(rhs_span.error("mismatch in number of object keysin lhs and rhs"));
|
||||
}
|
||||
|
||||
for ((_, lhs_key, lhs_value), (_, rhs_key, rhs_value)) in
|
||||
std::iter::zip(lhs_fields.iter(), rhs_fields.iter())
|
||||
{
|
||||
if self.eval_bool_expr(&BoolOp::Eq, lhs_key, rhs_key)?
|
||||
!= Value::Bool(true)
|
||||
{
|
||||
return Ok(Value::Bool(false));
|
||||
}
|
||||
|
||||
if self.eval_assign_expr(&AssignOp::Eq, lhs_value, rhs_value)?
|
||||
!= Value::Bool(true)
|
||||
{
|
||||
return Ok(Value::Bool(false));
|
||||
}
|
||||
}
|
||||
return Ok(Value::Bool(true));
|
||||
}
|
||||
(Expr::Array { .. }, _) => {
|
||||
let value = self.eval_expr(rhs)?;
|
||||
@@ -645,6 +671,7 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::Undefined | Value::Null => r = false,
|
||||
// Other types cause every to evaluate to true even though
|
||||
// it is supposed to happen only for empty domain.
|
||||
_ => (),
|
||||
@@ -715,7 +742,6 @@ impl Interpreter {
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
// Destructure objects
|
||||
(Expr::Object { fields, .. }, Value::Object(_)) => {
|
||||
let mut r = true;
|
||||
@@ -746,6 +772,10 @@ impl Interpreter {
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
// TODO: This suppresses errors in case of type mismatches.
|
||||
// OPA raises the error sometimes in static scenarios, but doesn't
|
||||
// raise in scenarios due to data/input
|
||||
(Expr::Array { .. }, _) | (Expr::Object { .. }, _) => Ok(false),
|
||||
_ => {
|
||||
let expr_value = self.lookup_or_eval_expr(cache, expr)?;
|
||||
if expr_value == Value::Undefined {
|
||||
@@ -1622,16 +1652,19 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
fn lookup_builtin(&self, span: &Span, path: &str) -> Result<Option<&BuiltinFcn>> {
|
||||
Ok(if let Some(builtin) = builtins::BUILTINS.get(path) {
|
||||
Some(builtin)
|
||||
} else if let Some(builtin) = builtins::DEPRECATED.get(path) {
|
||||
if let Some(builtin) = builtins::BUILTINS.get(path) {
|
||||
return Ok(Some(builtin));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deprecated")]
|
||||
if let Some(builtin) = builtins::DEPRECATED.get(path) {
|
||||
if !self.allow_deprecated {
|
||||
bail!(span.error(format!("{path} is deprecated").as_str()))
|
||||
}
|
||||
Some(builtin)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
return Ok(Some(builtin));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn eval_call_impl(&mut self, span: &Span, fcn: &ExprRef, params: &[ExprRef]) -> Result<Value> {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use std::cmp::{Ord, Ordering};
|
||||
use std::ops::{AddAssign, Div, MulAssign, Rem, SubAssign};
|
||||
use std::rc::Rc;
|
||||
@@ -56,13 +57,47 @@ impl Serialize for BigDecimal {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[derive(Clone)]
|
||||
pub enum Number {
|
||||
// TODO: maybe specialize for u64, i64, f64
|
||||
Big(Rc<BigDecimal>),
|
||||
}
|
||||
|
||||
impl Debug for Number {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
Number::Big(b) => b.d.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Number {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
Big(b) => {
|
||||
if let Some(n) = self.as_u64() {
|
||||
n.serialize(serializer)
|
||||
} else if let Some(n) = self.as_i64() {
|
||||
n.serialize(serializer)
|
||||
} else {
|
||||
if let Some(f) = self.as_f64() {
|
||||
if &Number::from(f) == self {
|
||||
return f.serialize(serializer);
|
||||
}
|
||||
}
|
||||
let s = b.d.to_string();
|
||||
let v = serde_json::Number::from_str(&s)
|
||||
.map_err(|_| serde::ser::Error::custom("could not serialize big number"))?;
|
||||
v.serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use Number::*;
|
||||
|
||||
impl From<u64> for Number {
|
||||
|
||||
@@ -76,7 +76,7 @@ impl<'source> Parser<'source> {
|
||||
|
||||
pub fn set_future_keyword(&mut self, kw: &str, span: &Span) -> Result<()> {
|
||||
match &self.future_keywords.get(kw) {
|
||||
Some(s) => Err(self.source.error(
|
||||
Some(s) if false => Err(self.source.error(
|
||||
span.line,
|
||||
span.col,
|
||||
format!(
|
||||
@@ -86,8 +86,11 @@ impl<'source> Parser<'source> {
|
||||
)
|
||||
.as_str(),
|
||||
)),
|
||||
None => {
|
||||
_ => {
|
||||
self.future_keywords.insert(kw.to_string(), span.clone());
|
||||
if kw == "every" {
|
||||
self.future_keywords.insert("in".to_string(), span.clone());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1475,6 +1478,11 @@ impl<'source> Parser<'source> {
|
||||
let ref_comps = Self::get_path_ref_components(&import.refr)?;
|
||||
let comps: Vec<std::rc::Rc<&str>> = ref_comps.iter().map(|s| s.text()).collect();
|
||||
|
||||
if comps.len() >= 2 && comps[0].as_ref() == &"future" && comps[1].as_ref() == &"keywords" {
|
||||
imports.push(import);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for imp in imports.iter() {
|
||||
let imp_comps = Self::get_path_ref_components(&imp.refr)?;
|
||||
let imp_comps: Vec<std::rc::Rc<&str>> = imp_comps.iter().map(|s| s.text()).collect();
|
||||
|
||||
11
src/utils.rs
11
src/utils.rs
@@ -51,12 +51,17 @@ fn get_extra_arg_impl(
|
||||
*n_args
|
||||
} else {
|
||||
let path = get_path_string(fcn, None)?;
|
||||
if let Some((_, n_args)) = BUILTINS.get(path.as_str()) {
|
||||
if let Some((_, n_args)) = functions.get(&path) {
|
||||
*n_args
|
||||
} else if let Some((_, n_args)) = DEPRECATED.get(path.as_str()) {
|
||||
} else if let Some((_, n_args)) = BUILTINS.get(path.as_str()) {
|
||||
*n_args
|
||||
} else {
|
||||
return Ok(None);
|
||||
#[cfg(feature = "deprecated")]
|
||||
if let Some((_, n_args)) = DEPRECATED.get(path.as_str()) {
|
||||
*n_args
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (n_args as usize) + 1 == params.len() {
|
||||
|
||||
@@ -223,10 +223,12 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
pub fn from_yaml_str(yaml: &str) -> Result<Value> {
|
||||
Ok(serde_yaml::from_str(yaml)?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
pub fn from_yaml_file(path: &String) -> Result<Value> {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(c) => Self::from_yaml_str(c.as_str()),
|
||||
|
||||
@@ -80,9 +80,6 @@ cases:
|
||||
every _, _ in 1 {
|
||||
false
|
||||
}
|
||||
every _, _ in null {
|
||||
false
|
||||
}
|
||||
every _, _ in false {
|
||||
false
|
||||
}
|
||||
@@ -91,10 +88,26 @@ cases:
|
||||
}
|
||||
every _ in `abc` {
|
||||
undefined_var
|
||||
}
|
||||
y = 100
|
||||
}
|
||||
|
||||
|
||||
x5 = y {
|
||||
every _ in null {
|
||||
false
|
||||
}
|
||||
y = 100
|
||||
}
|
||||
|
||||
x6 = y {
|
||||
# Input is null
|
||||
every _ in input {
|
||||
false
|
||||
}
|
||||
y = 100
|
||||
}
|
||||
|
||||
undefined_var { false }
|
||||
query: data.test
|
||||
want_result:
|
||||
|
||||
@@ -65,6 +65,8 @@ cases:
|
||||
x { some [1] in [1] }
|
||||
query: data.test
|
||||
error: "Cannot bind pattern of type `array`"
|
||||
# TODO: How to raise errors only for static mismatches.
|
||||
skip: true
|
||||
|
||||
- note: array-length-mismatch
|
||||
data: {}
|
||||
|
||||
@@ -14,6 +14,14 @@ comparisonexpr
|
||||
completedoc
|
||||
compositebasedereference
|
||||
dataderef
|
||||
cryptohmacequal
|
||||
cryptohmacmd5
|
||||
cryptohmacsha1
|
||||
cryptohmacsha256
|
||||
cryptohmacsha512
|
||||
cryptomd5
|
||||
cryptsha1
|
||||
cryptosha256
|
||||
embeddedvirtualdoc
|
||||
evaltermexpr
|
||||
example
|
||||
|
||||
@@ -124,21 +124,27 @@ cases:
|
||||
package test
|
||||
import future.keywords
|
||||
import future["keywords"]
|
||||
error: "this import shadows previous import of `contains`"
|
||||
#error: "this import shadows previous import of `contains`"
|
||||
query: data.test
|
||||
want_result: {}
|
||||
|
||||
- note: shadow/1
|
||||
rego: |
|
||||
package test
|
||||
import future.keywords
|
||||
import future.keywords.if
|
||||
error: "this import shadows previous import of `if`"
|
||||
#error: "this import shadows previous import of `if`"
|
||||
query: data.test
|
||||
want_result: {}
|
||||
|
||||
- note: shadow/2
|
||||
rego: |
|
||||
package test
|
||||
import future.keywords.if
|
||||
import future.keywords
|
||||
error: "this import shadows previous import of `if`"
|
||||
#error: "this import shadows previous import of `if`"
|
||||
query: data.test
|
||||
want_result: {}
|
||||
|
||||
- note: in-as-var
|
||||
rego: |
|
||||
|
||||
Reference in New Issue
Block a user