Skip to content

Commit 1df10e6

Browse files
committed
Validate registered identifiers
1 parent af70cef commit 1df10e6

3 files changed

Lines changed: 174 additions & 38 deletions

File tree

‎engine/src/lib.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ pub use self::rhs_types::{
118118
};
119119
pub use self::scheme::{
120120
Field, FieldIndex, FieldRedefinitionError, FieldRef, Function, FunctionRedefinitionError,
121-
FunctionRef, IdentifierRedefinitionError, IndexAccessError, List, ListRef, Scheme,
122-
SchemeBuilder, SchemeMismatchError, UnknownFieldError,
121+
FunctionRef, IdentifierRedefinitionError, IdentifierRegistrationError, IndexAccessError, List,
122+
ListRef, Scheme, SchemeBuilder, SchemeMismatchError, UnknownFieldError,
123123
};
124124
pub use self::types::{
125125
CompoundType, ExpectedType, ExpectedTypeList, GetType, LhsValue, LiteralSet, LiteralValue,

‎engine/src/scheme.rs‎

Lines changed: 159 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -426,22 +426,8 @@ pub(crate) enum Identifier<'s> {
426426
}
427427

428428
impl<'i, 's> LexWith<'i, &'s Scheme> for Identifier<'s> {
429-
fn lex_with(mut input: &'i str, scheme: &'s Scheme) -> LexResult<'i, Self> {
430-
let initial_input = input;
431-
432-
loop {
433-
input = take_while(input, "identifier character", |c| {
434-
c.is_ascii_alphanumeric() || c == '_'
435-
})?
436-
.1;
437-
438-
match expect(input, ".") {
439-
Ok(rest) => input = rest,
440-
Err(_) => break,
441-
};
442-
}
443-
444-
let name = span(initial_input, input);
429+
fn lex_with(input: &'i str, scheme: &'s Scheme) -> LexResult<'i, Self> {
430+
let (name, input) = lex_identifier_name(input)?;
445431

446432
let field = scheme
447433
.get(name)
@@ -451,6 +437,25 @@ impl<'i, 's> LexWith<'i, &'s Scheme> for Identifier<'s> {
451437
}
452438
}
453439

440+
#[inline]
441+
fn lex_identifier_name(mut input: &str) -> LexResult<'_, &str> {
442+
let initial_input = input;
443+
444+
loop {
445+
input = take_while(input, "identifier character", |c| {
446+
c.is_ascii_alphanumeric() || c == '_'
447+
})?
448+
.1;
449+
450+
match expect(input, ".") {
451+
Ok(rest) => input = rest,
452+
Err(_) => break,
453+
};
454+
}
455+
456+
Ok((span(initial_input, input), input))
457+
}
458+
454459
/// An error that occurs if an unregistered field name was queried from a
455460
/// [`Scheme`](struct@Scheme).
456461
#[derive(Debug, PartialEq, Eq, Error)]
@@ -485,6 +490,28 @@ pub enum IdentifierRedefinitionError {
485490
Function(#[source] FunctionRedefinitionError),
486491
}
487492

493+
/// An error that occurs when registering a field or function identifier.
494+
#[derive(Debug, PartialEq, Eq, Error)]
495+
pub enum IdentifierRegistrationError {
496+
/// The identifier does not follow the syntax accepted for registration.
497+
#[error("invalid identifier {0}")]
498+
InvalidIdentifier(String),
499+
500+
/// The identifier is already registered as a field or function.
501+
#[error("{0}")]
502+
Redefinition(#[from] IdentifierRedefinitionError),
503+
}
504+
505+
fn is_valid_identifier(name: &str) -> bool {
506+
matches!(lex_identifier_name(name), Ok((_, "")))
507+
&& matches!(name.as_bytes().first(), Some(b) if b.is_ascii_alphabetic() || *b == b'_')
508+
&& name.split('.').all(|segment| {
509+
segment
510+
.bytes()
511+
.any(|b| b.is_ascii_alphabetic() || b == b'_')
512+
})
513+
}
514+
488515
#[derive(Clone, Copy, Debug)]
489516
enum SchemeItem {
490517
Field(usize),
@@ -647,16 +674,23 @@ impl SchemeBuilder {
647674
name: Arc<str>,
648675
ty: Type,
649676
optional: bool,
650-
) -> Result<(), IdentifierRedefinitionError> {
677+
) -> Result<(), IdentifierRegistrationError> {
678+
if !is_valid_identifier(&name) {
679+
return Err(IdentifierRegistrationError::InvalidIdentifier(
680+
name.to_string(),
681+
));
682+
}
683+
651684
match self.items.entry(name) {
652-
Entry::Occupied(entry) => match entry.get() {
653-
SchemeItem::Field(_) => Err(IdentifierRedefinitionError::Field(
654-
FieldRedefinitionError(entry.key().to_string()),
685+
Entry::Occupied(entry) => Err(match entry.get() {
686+
SchemeItem::Field(_) => IdentifierRedefinitionError::Field(FieldRedefinitionError(
687+
entry.key().to_string(),
655688
)),
656-
SchemeItem::Function(_) => Err(IdentifierRedefinitionError::Function(
689+
SchemeItem::Function(_) => IdentifierRedefinitionError::Function(
657690
FunctionRedefinitionError(entry.key().to_string()),
658-
)),
659-
},
691+
),
692+
}
693+
.into()),
660694
Entry::Vacant(entry) => {
661695
let index = self.fields.len();
662696
self.fields.push(FieldDefinition {
@@ -675,7 +709,7 @@ impl SchemeBuilder {
675709
&mut self,
676710
name: N,
677711
ty: Type,
678-
) -> Result<(), IdentifierRedefinitionError> {
712+
) -> Result<(), IdentifierRegistrationError> {
679713
self.add_field_full(name.as_ref().into(), ty, false)
680714
}
681715

@@ -684,7 +718,7 @@ impl SchemeBuilder {
684718
&mut self,
685719
name: N,
686720
ty: Type,
687-
) -> Result<(), IdentifierRedefinitionError> {
721+
) -> Result<(), IdentifierRegistrationError> {
688722
self.add_field_full(name.as_ref().into(), ty, true)
689723
}
690724

@@ -693,16 +727,24 @@ impl SchemeBuilder {
693727
&mut self,
694728
name: N,
695729
function: impl FunctionDefinition + 'static,
696-
) -> Result<(), IdentifierRedefinitionError> {
697-
match self.items.entry(name.as_ref().into()) {
698-
Entry::Occupied(entry) => match entry.get() {
699-
SchemeItem::Field(_) => Err(IdentifierRedefinitionError::Field(
700-
FieldRedefinitionError(entry.key().to_string()),
730+
) -> Result<(), IdentifierRegistrationError> {
731+
let name = name.as_ref();
732+
if !is_valid_identifier(name) {
733+
return Err(IdentifierRegistrationError::InvalidIdentifier(
734+
name.to_string(),
735+
));
736+
}
737+
738+
match self.items.entry(name.into()) {
739+
Entry::Occupied(entry) => Err(match entry.get() {
740+
SchemeItem::Field(_) => IdentifierRedefinitionError::Field(FieldRedefinitionError(
741+
entry.key().to_string(),
701742
)),
702-
SchemeItem::Function(_) => Err(IdentifierRedefinitionError::Function(
743+
SchemeItem::Function(_) => IdentifierRedefinitionError::Function(
703744
FunctionRedefinitionError(entry.key().to_string()),
704-
)),
705-
},
745+
),
746+
}
747+
.into()),
706748
Entry::Vacant(entry) => {
707749
let index = self.functions.len();
708750
self.functions
@@ -1854,10 +1896,80 @@ fn test_field_type_override() {
18541896

18551897
assert_eq!(
18561898
builder.add_field("foo", Type::Bytes),
1857-
Err(IdentifierRedefinitionError::Field(FieldRedefinitionError(
1858-
"foo".into()
1859-
)))
1899+
Err(IdentifierRegistrationError::Redefinition(
1900+
IdentifierRedefinitionError::Field(FieldRedefinitionError("foo".into()))
1901+
))
1902+
);
1903+
}
1904+
1905+
#[test]
1906+
fn test_identifier_registration() {
1907+
use crate::ConcatFunction;
1908+
1909+
let mut builder = SchemeBuilder::new();
1910+
let valid_field_names = [
1911+
"_",
1912+
"field",
1913+
"Field_0",
1914+
"http._request",
1915+
"a.b2._c3",
1916+
"cf.response.1xxx_code",
1917+
];
1918+
for name in valid_field_names {
1919+
assert_eq!(builder.add_field(name, Type::Bytes), Ok(()));
1920+
}
1921+
assert_eq!(
1922+
builder.add_optional_field("optional.field", Type::Int),
1923+
Ok(())
1924+
);
1925+
assert_eq!(
1926+
builder.add_function("function.name", ConcatFunction::new()),
1927+
Ok(())
1928+
);
1929+
1930+
for name in [
1931+
"",
1932+
".field",
1933+
"field.",
1934+
"field..name",
1935+
"0field",
1936+
"123",
1937+
"192.0.2.1",
1938+
"a.1",
1939+
"a.123",
1940+
"a.1.2",
1941+
"f-ield",
1942+
"féield",
1943+
] {
1944+
assert_eq!(
1945+
builder.add_field(name, Type::Bytes),
1946+
Err(IdentifierRegistrationError::InvalidIdentifier(name.into()))
1947+
);
1948+
}
1949+
1950+
assert_eq!(
1951+
builder.add_optional_field("optional.", Type::Int),
1952+
Err(IdentifierRegistrationError::InvalidIdentifier(
1953+
"optional.".into()
1954+
))
1955+
);
1956+
assert_eq!(
1957+
builder.add_function("1function", ConcatFunction::new()),
1958+
Err(IdentifierRegistrationError::InvalidIdentifier(
1959+
"1function".into()
1960+
))
18601961
);
1962+
1963+
let scheme = builder.build();
1964+
for name in valid_field_names.into_iter().chain(["optional.field"]) {
1965+
let (field, rest) = FieldRef::lex_with(name, &scheme).unwrap();
1966+
assert_eq!(field.name(), name);
1967+
assert_eq!(rest, "");
1968+
}
1969+
1970+
let (function, rest) = FunctionRef::lex_with("function.name", &scheme).unwrap();
1971+
assert_eq!(function.name(), "function.name");
1972+
assert_eq!(rest, "");
18611973
}
18621974

18631975
#[test]
@@ -1920,6 +2032,17 @@ fn test_scheme_json_serialization() {
19202032
assert_eq!(scheme.inner.fields, new_scheme.inner.fields);
19212033
}
19222034

2035+
#[test]
2036+
fn test_scheme_json_rejects_invalid_identifier() {
2037+
let scheme = Scheme! { valid: Int }.build();
2038+
let json = serde_json::to_string(&scheme)
2039+
.unwrap()
2040+
.replace("\"valid\"", "\"1invalid\"");
2041+
2042+
let err = serde_json::from_str::<Scheme>(&json).unwrap_err();
2043+
assert!(err.to_string().contains("invalid identifier 1invalid"));
2044+
}
2045+
19232046
#[test]
19242047
fn test_nil_not_equal_behavior_true() {
19252048
use crate::{Array, ExecutionContext, Map};

‎ffi/src/lib.rs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,19 @@ mod ffi_test {
888888
wirefilter_build_scheme(builder)
889889
}
890890

891+
#[test]
892+
fn reject_invalid_field_identifier() {
893+
let mut builder = wirefilter_create_scheme_builder();
894+
let name = "2http.host";
895+
896+
assert!(!wirefilter_add_type_field_to_scheme(
897+
&mut builder,
898+
name.as_ptr().cast(),
899+
name.len(),
900+
Type::Bytes.into(),
901+
));
902+
}
903+
891904
fn create_execution_context<'e, 's: 'e>(scheme: &'s Scheme) -> Box<ExecutionContext<'e>> {
892905
let mut exec_context = wirefilter_create_execution_context(scheme);
893906
let invalid_key = &b"\xc3\x28"[..];

0 commit comments

Comments
 (0)