diff --git a/crates/bashkit/src/interpreter/mod.rs b/crates/bashkit/src/interpreter/mod.rs index c79612c9..7b18d315 100644 --- a/crates/bashkit/src/interpreter/mod.rs +++ b/crates/bashkit/src/interpreter/mod.rs @@ -6681,6 +6681,10 @@ impl Interpreter { } /// Replace pattern in value + /// THREAT[TM-DOS]: Maximum expansion result size (10MB) to prevent memory + /// amplification in global pattern replacement. + const MAX_EXPANSION_RESULT_BYTES: usize = 10 * 1024 * 1024; + fn replace_pattern( &self, value: &str, @@ -6747,8 +6751,12 @@ impl Interpreter { if let Some(pos) = value.find(suffix) { let after = &value[pos + suffix.len()..]; if global { - return replacement.to_string() + let result = replacement.to_string() + &self.replace_pattern(after, pattern, replacement, true); + if result.len() > Self::MAX_EXPANSION_RESULT_BYTES { + return value.to_string(); + } + return result; } else { return replacement.to_string() + after; } @@ -6766,7 +6774,11 @@ impl Interpreter { // Simple string replacement if global { - value.replace(pattern, replacement) + let result = value.replace(pattern, replacement); + if result.len() > Self::MAX_EXPANSION_RESULT_BYTES { + return value.to_string(); + } + result } else { value.replacen(pattern, replacement, 1) } diff --git a/crates/bashkit/tests/spec_cases/bash/replace_pattern_limit.test.sh b/crates/bashkit/tests/spec_cases/bash/replace_pattern_limit.test.sh new file mode 100644 index 00000000..eea51d42 --- /dev/null +++ b/crates/bashkit/tests/spec_cases/bash/replace_pattern_limit.test.sh @@ -0,0 +1,26 @@ +# Global pattern replacement size limit +# Regression tests for issue #995 + +### normal_global_replacement_works +# Normal global replacement still works +val="aaa" +echo "${val//a/bb}" +### expect +bbbbbb +### end + +### single_replacement_works +# Single replacement unaffected by limit +val="hello world" +echo "${val/world/universe}" +### expect +hello universe +### end + +### global_replacement_empty_pattern +# Empty pattern returns original +val="test" +echo "${val//}" +### expect +test +### end