Remove double parsing of data pointed to

As there was an issues with the parser which checked the validity of
the JSON pointer pointing to the received JSON data this part is
removed. Further some checks were added to double check that case
which lead to an invalid behaviour.

This fixes #9.
This commit is contained in:
finga 2021-11-06 11:03:27 +01:00
parent 87d6f58f72
commit c82c0fcbd5

View file

@ -43,8 +43,6 @@ enum WebhookeyError {
Unauthorized(IpAddr), Unauthorized(IpAddr),
#[error("Unmatched hook from `{0}`")] #[error("Unmatched hook from `{0}`")]
UnmatchedHook(IpAddr), UnmatchedHook(IpAddr),
#[error("Could not find field refered to in parameter `{0}`")]
InvalidParameterPointer(String),
#[error("Could not evaluate filter request")] #[error("Could not evaluate filter request")]
InvalidFilter, InvalidFilter,
#[error("IO Error")] #[error("IO Error")]
@ -218,20 +216,10 @@ impl Hook {
&self, &self,
hook_name: &str, hook_name: &str,
request: &Request, request: &Request,
data: &mut serde_json::Value, data: &serde_json::Value,
) -> Result<String> { ) -> Result<String> {
trace!("Replacing parameters for command of hook `{}`", hook_name); trace!("Replacing parameters for command of hook `{}`", hook_name);
for parameter in get_parameter(&self.command)? {
let parameter = parameter.trim();
if let Some(json_value) = data.pointer(parameter) {
*data.pointer_mut(parameter).ok_or_else(|| {
WebhookeyError::InvalidParameterPointer(parameter.to_string())
})? = serde_json::Value::String(get_string(json_value)?);
}
}
replace_parameters(&self.command, request.headers(), data) replace_parameters(&self.command, request.headers(), data)
} }
} }
@ -288,11 +276,11 @@ impl Hooks {
valid = true; valid = true;
let mut data: serde_json::Value = let data: serde_json::Value =
serde_json::from_slice(&buffer).map_err(WebhookeyError::Serde)?; serde_json::from_slice(&buffer).map_err(WebhookeyError::Serde)?;
match hook.filter.evaluate(request, &data) { match hook.filter.evaluate(request, &data) {
Ok(true) => match hook.get_command(hook_name, request, &mut data) { Ok(true) => match hook.get_command(hook_name, request, &data) {
Ok(command) => { Ok(command) => {
info!("Filter for `{}` matched", &hook_name); info!("Filter for `{}` matched", &hook_name);
result.insert(hook_name.to_string(), command); result.insert(hook_name.to_string(), command);
@ -370,19 +358,6 @@ fn validate_request(secret: &str, signature: &str, data: &[u8]) -> Result<()> {
mac.verify(&raw_signature).map_err(|e| anyhow!("{}", e)) mac.verify(&raw_signature).map_err(|e| anyhow!("{}", e))
} }
fn get_parameter(input: &str) -> Result<Vec<&str>> {
let parse: IResult<&str, Vec<&str>> = many0(alt((
delimited(tag("{{"), take_until("}}"), tag("}}")),
take_until("{{"),
)))(input);
let (_last, result) = parse
.finish()
.map_err(|e| anyhow!("Could not get parameters from command: {}", e))?;
Ok(result)
}
fn get_header_field<'a>(headers: &'a HeaderMap, param: &[&str]) -> Result<&'a str> { fn get_header_field<'a>(headers: &'a HeaderMap, param: &[&str]) -> Result<&'a str> {
headers headers
.get_one( .get_one(
@ -691,5 +666,145 @@ mod tests {
.unwrap(), .unwrap(),
" something command" " something command"
); );
assert_eq!(
replace_parameters(
" {{ header X-Gitea-Event }} {{ /field1/foo }} command",
&headers,
&json!({ "field1": { "foo": "bar" } })
)
.unwrap(),
" something bar command"
);
}
#[rocket::async_test]
async fn parse_command_request() {
let mut hooks = HashMap::new();
hooks.insert(
"test_hook".to_string(),
Hook {
command:
"/usr/bin/echo {{ /repository/full_name }} --foo {{ /pull_request/base/ref }}"
.to_string(),
signature: "X-Gitea-Signature".to_string(),
ip_filter: None,
secrets: vec!["valid".to_string()],
filter: FilterType::JsonFilter(JsonFilter {
pointer: "/foo".to_string(),
regex: "bar".to_string(),
}),
},
);
hooks.insert(
"test_hook".to_string(),
Hook {
command: "/usr/bin/echo {{ /repository/full_name }} {{ /pull_request/base/ref }}"
.to_string(),
signature: "X-Gitea-Signature".to_string(),
ip_filter: None,
secrets: vec!["valid".to_string()],
filter: FilterType::JsonFilter(JsonFilter {
pointer: "/foo".to_string(),
regex: "bar".to_string(),
}),
},
);
let config = Config {
// default: None,
hooks: hooks,
};
let rocket = rocket::build()
.mount("/", routes![receive_hook])
.manage(config);
let client = Client::tracked(rocket).await.unwrap();
let response = client
.post("/")
.header(Header::new(
"X-Gitea-Signature",
"693b733871ecb684651a813c82936df683c9e4a816581f385353e06170545400",
))
.header(ContentType::JSON)
.remote("127.0.0.1:8000".parse().unwrap())
.body(
&serde_json::to_string(&json!({
"foo": "bar",
"repository": {
"full_name": "keith"
},
"pull_request": {
"base": {
"ref": "main"
}
}
}))
.unwrap(),
)
.dispatch();
assert_eq!(response.await.status(), Status::Ok);
}
#[rocket::async_test]
async fn parse_invalid_command_request() {
let mut hooks = HashMap::new();
hooks.insert(
"test_hook".to_string(),
Hook {
command: "/usr/bin/echo {{ /repository/full }} {{ /pull_request/base/ref }}"
.to_string(),
signature: "X-Gitea-Signature".to_string(),
ip_filter: None,
secrets: vec!["valid".to_string()],
filter: FilterType::JsonFilter(JsonFilter {
pointer: "/foo".to_string(),
regex: "bar".to_string(),
}),
},
);
let config = Config {
// default: None,
hooks: hooks,
};
let rocket = rocket::build()
.mount("/", routes![receive_hook])
.manage(config);
let client = Client::tracked(rocket).await.unwrap();
let response = client
.post("/")
.header(Header::new(
"X-Gitea-Signature",
"693b733871ecb684651a813c82936df683c9e4a816581f385353e06170545400",
))
.header(ContentType::JSON)
.remote("127.0.0.1:8000".parse().unwrap())
.body(
&serde_json::to_string(&json!({
"foo": "bar",
"repository": {
"full_name": "keith"
},
"pull_request": {
"base": {
"ref": "main"
}
}
}))
.unwrap(),
)
.dispatch();
assert_eq!(response.await.status(), Status::NotFound);
} }
} }