From f033793f74f09fa101c69b4386e5b5c75deb9b02 Mon Sep 17 00:00:00 2001 From: Orne Brocaar Date: Tue, 19 Apr 2022 12:05:32 +0100 Subject: [PATCH] Make js codec execution time configurable. Closes #1. --- chirpstack/src/cmd/configfile.rs | 10 ++++++ chirpstack/src/codec/js.rs | 53 ++++++++++++++++++++++++++++++-- chirpstack/src/config.rs | 23 ++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/chirpstack/src/cmd/configfile.rs b/chirpstack/src/cmd/configfile.rs index ab479a40..f08323a7 100644 --- a/chirpstack/src/cmd/configfile.rs +++ b/chirpstack/src/cmd/configfile.rs @@ -298,6 +298,16 @@ pub fn run() { tls_key="{{ integration.mqtt.tls_key }}" +# Codec configuration. +[codec] + + # JS codec configuration. + [codec.js] + + # Maximum execution time. + max_execution_time="{{ codec.js.max_execution_time }}" + + # User authentication configuration. [user_authentication] diff --git a/chirpstack/src/codec/js.rs b/chirpstack/src/codec/js.rs index e335acf8..e8af9ad7 100644 --- a/chirpstack/src/codec/js.rs +++ b/chirpstack/src/codec/js.rs @@ -1,9 +1,11 @@ use std::collections::HashMap; +use std::time::SystemTime; use anyhow::Result; use rquickjs::IntoJs; use super::convert; +use crate::config; pub async fn decode( f_port: u8, @@ -11,7 +13,12 @@ pub async fn decode( decode_config: &str, b: &[u8], ) -> Result { + let conf = config::get(); + let max_run_ts = SystemTime::now() + conf.codec.js.max_execution_time; + let rt = rquickjs::Runtime::new().unwrap(); + rt.set_interrupt_handler(Some(Box::new(move || SystemTime::now() > max_run_ts))); + let ctx = rquickjs::Context::full(&rt).unwrap(); let script = decode_config.to_string(); @@ -28,7 +35,7 @@ pub async fn decode( .unwrap(); input.set("data", b.into_js(ctx).unwrap()).unwrap(); - let res: rquickjs::Object = func.call((input,)).unwrap(); + let res: rquickjs::Object = func.call((input,))?; Ok(convert::rquickjs_to_struct(&res)) }) } @@ -39,7 +46,12 @@ pub async fn encode( encode_config: &str, s: &prost_types::Struct, ) -> Result> { + let conf = config::get(); + let max_run_ts = SystemTime::now() + conf.codec.js.max_execution_time; + let rt = rquickjs::Runtime::new().unwrap(); + rt.set_interrupt_handler(Some(Box::new(move || SystemTime::now() > max_run_ts))); + let ctx = rquickjs::Context::full(&rt).unwrap(); let script = encode_config.to_string(); @@ -57,7 +69,7 @@ pub async fn encode( .set("object", convert::struct_to_rquickjs(ctx, s)) .unwrap(); - let res: Vec = func.call((input,)).unwrap(); + let res: Vec = func.call((input,))?; Ok(res) }) } @@ -66,6 +78,22 @@ pub async fn encode( pub mod test { use super::*; + #[tokio::test] + pub async fn test_decode_timeout() { + let decoder = r#" + export function Decode(input) { + while (true) { + + } + } + "# + .to_string(); + + let vars: HashMap = HashMap::new(); + let out = decode(10, &vars, &decoder, &[0x01, 0x02, 0x03]).await; + assert!(out.is_err()); + } + #[tokio::test] pub async fn test_decode() { let decoder = r#" @@ -143,6 +171,27 @@ pub mod test { assert_eq!(expected, out); } + #[tokio::test] + pub async fn test_encode_timeout() { + let encoder = r#" + export function Encode(input) { + while (true) { + + } + } + "# + .to_string(); + + let vars: HashMap = HashMap::new(); + + let input = prost_types::Struct { + ..Default::default() + }; + + let out = encode(10, &vars, &encoder, &input).await; + assert!(out.is_err()); + } + #[tokio::test] pub async fn test_encode() { let encoder = r#" diff --git a/chirpstack/src/config.rs b/chirpstack/src/config.rs index af812a77..d7011e60 100644 --- a/chirpstack/src/config.rs +++ b/chirpstack/src/config.rs @@ -24,6 +24,7 @@ pub struct Configuration { pub network: Network, pub monitoring: Monitoring, pub integration: Integration, + pub codec: Codec, pub user_authentication: UserAuthentication, pub join_server: JoinServer, pub keks: Vec, @@ -41,6 +42,7 @@ impl Default for Configuration { network: Default::default(), monitoring: Default::default(), integration: Default::default(), + codec: Default::default(), user_authentication: Default::default(), join_server: Default::default(), keks: Vec::new(), @@ -293,6 +295,27 @@ impl Default for MqttIntegrationClient { } } +#[derive(Serialize, Deserialize, Clone, Default)] +#[serde(default)] +pub struct Codec { + pub js: CodecJs, +} + +#[derive(Serialize, Deserialize, Clone)] +#[serde(default)] +pub struct CodecJs { + #[serde(with = "humantime_serde")] + pub max_execution_time: Duration, +} + +impl Default for CodecJs { + fn default() -> Self { + CodecJs { + max_execution_time: Duration::from_millis(100), + } + } +} + #[derive(Serialize, Deserialize, Clone, Default)] #[serde(default)] pub struct UserAuthentication {