fix: INI newline injection in backtest config generation
Add ini_safe() helper that strips CR/LF from user-supplied string params before they are written into terminal.ini and backtest_config.ini. Applies to: expert path, symbol, timeframe, set_file — any value that could carry an embedded newline and inject extra INI directives. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
05214ff34b
commit
b6171d0c56
Generated
+1
-1
@@ -481,7 +481,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mt5-quant"
|
name = "mt5-quant"
|
||||||
version = "1.32.2"
|
version = "1.32.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@@ -823,12 +823,12 @@ impl BacktestPipeline {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let set_file_line = params.set_file.as_ref()
|
let set_file_line = params.set_file.as_ref()
|
||||||
.map(|p| format!("ExpertParameters={}\n", p))
|
.map(|p| format!("ExpertParameters={}\n", Self::ini_safe(p)))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let updates: &[(&str, String)] = &[
|
let updates: &[(&str, String)] = &[
|
||||||
("Expert", expert_path),
|
("Expert", Self::ini_safe(&expert_path)),
|
||||||
("Symbol", params.symbol.clone()),
|
("Symbol", Self::ini_safe(¶ms.symbol)),
|
||||||
("Period", period.to_string()),
|
("Period", period.to_string()),
|
||||||
("DateRange", "3".into()),
|
("DateRange", "3".into()),
|
||||||
("DateFrom", from_ts.to_string()),
|
("DateFrom", from_ts.to_string()),
|
||||||
@@ -855,6 +855,11 @@ impl BacktestPipeline {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Strip CR/LF from a user-supplied INI value to prevent newline injection.
|
||||||
|
fn ini_safe(value: &str) -> String {
|
||||||
|
value.replace(['\n', '\r'], "")
|
||||||
|
}
|
||||||
|
|
||||||
fn patch_ini_section(text: &str, section: &str, updates: &[(&str, String)]) -> String {
|
fn patch_ini_section(text: &str, section: &str, updates: &[(&str, String)]) -> String {
|
||||||
let section_header = format!("[{}]", section);
|
let section_header = format!("[{}]", section);
|
||||||
let mut result = String::with_capacity(text.len() + 256);
|
let mut result = String::with_capacity(text.len() + 256);
|
||||||
@@ -1025,9 +1030,9 @@ impl BacktestPipeline {
|
|||||||
|
|
||||||
ini.push_str("[Tester]\n");
|
ini.push_str("[Tester]\n");
|
||||||
// Expert path is relative to MQL5/Experts/ in the /config: format (no "Experts\" prefix).
|
// Expert path is relative to MQL5/Experts/ in the /config: format (no "Experts\" prefix).
|
||||||
ini.push_str(&format!("Expert={}\n", self.resolve_backtest_ini_expert_path(¶ms.expert)));
|
ini.push_str(&format!("Expert={}\n", Self::ini_safe(&self.resolve_backtest_ini_expert_path(¶ms.expert))));
|
||||||
ini.push_str(&format!("Symbol={}\n", params.symbol));
|
ini.push_str(&format!("Symbol={}\n", Self::ini_safe(¶ms.symbol)));
|
||||||
ini.push_str(&format!("Period={}\n", params.timeframe));
|
ini.push_str(&format!("Period={}\n", Self::ini_safe(¶ms.timeframe)));
|
||||||
ini.push_str("Optimization=0\n");
|
ini.push_str("Optimization=0\n");
|
||||||
ini.push_str(&format!("Model={}\n", params.model));
|
ini.push_str(&format!("Model={}\n", params.model));
|
||||||
ini.push_str(&format!("FromDate={}\n", params.from_date));
|
ini.push_str(&format!("FromDate={}\n", params.from_date));
|
||||||
@@ -1044,7 +1049,7 @@ impl BacktestPipeline {
|
|||||||
ini.push_str(&format!("ShutdownTerminal={}\n", if params.shutdown { "1" } else { "0" }));
|
ini.push_str(&format!("ShutdownTerminal={}\n", if params.shutdown { "1" } else { "0" }));
|
||||||
|
|
||||||
if let Some(set_file) = ¶ms.set_file {
|
if let Some(set_file) = ¶ms.set_file {
|
||||||
ini.push_str(&format!("ExpertParameters={}\n", set_file));
|
ini.push_str(&format!("ExpertParameters={}\n", Self::ini_safe(set_file)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ini)
|
Ok(ini)
|
||||||
|
|||||||
Reference in New Issue
Block a user