diff --git a/src/crewai/task.py b/src/crewai/task.py index a6a6fdeb2..a63bde57d 100644 --- a/src/crewai/task.py +++ b/src/crewai/task.py @@ -236,12 +236,13 @@ class Task(BaseModel): if ".." in value: raise ValueError("Path traversal attempts are not allowed in output_file paths") - if any(char in value for char in ['|', '>', '<', '&', ';', '$']): - raise ValueError("Shell special characters are not allowed in output_file paths") - - # Check for absolute paths that might bypass restrictions + # Check for shell expansion first if value.startswith('~') or value.startswith('$'): raise ValueError("Shell expansion characters are not allowed in output_file paths") + + # Then check other shell special characters + if any(char in value for char in ['|', '>', '<', '&', ';']): + raise ValueError("Shell special characters are not allowed in output_file paths") # Don't strip leading slash if it's a template path with variables if "{" in value or "}" in value: @@ -478,24 +479,25 @@ class Task(BaseModel): Args: input_string: The string containing template variables to interpolate. - Can be None, in which case an empty string is returned. + Can be None or empty, in which case an empty string is returned. inputs: Dictionary mapping template variables to their values. Supported value types are strings, integers, and floats. + If input_string is empty or has no placeholders, inputs can be empty. Returns: The interpolated string with all template variables replaced with their values. - Empty string if input_string is None. + Empty string if input_string is None or empty. Raises: ValueError: If a required template variable is missing from inputs. KeyError: If a template variable is not found in the inputs dictionary. """ - if input_string is None: + if input_string is None or not input_string: return "" - if not input_string: - raise ValueError("Input string cannot be empty when provided") + if "{" not in input_string and "}" not in input_string: + return input_string if not inputs: - raise ValueError("Inputs dictionary cannot be empty") + raise ValueError("Inputs dictionary cannot be empty when interpolating variables") try: # Validate input types diff --git a/tests/task_test.py b/tests/task_test.py index 198f53f3f..dc15c251f 100644 --- a/tests/task_test.py +++ b/tests/task_test.py @@ -880,20 +880,56 @@ def test_key(): def test_output_file_validation(): """Test output file path validation.""" # Valid paths - assert Task(output_file="output.txt").output_file == "output.txt" - assert Task(output_file="/tmp/output.txt").output_file == "tmp/output.txt" - assert Task(output_file="{dir}/output_{date}.txt").output_file == "{dir}/output_{date}.txt" + assert Task( + description="Test task", + expected_output="Test output", + output_file="output.txt" + ).output_file == "output.txt" + assert Task( + description="Test task", + expected_output="Test output", + output_file="/tmp/output.txt" + ).output_file == "tmp/output.txt" + assert Task( + description="Test task", + expected_output="Test output", + output_file="{dir}/output_{date}.txt" + ).output_file == "{dir}/output_{date}.txt" # Invalid paths with pytest.raises(ValueError, match="Path traversal"): - Task(output_file="../output.txt") + Task( + description="Test task", + expected_output="Test output", + output_file="../output.txt" + ) with pytest.raises(ValueError, match="Path traversal"): - Task(output_file="folder/../output.txt") + Task( + description="Test task", + expected_output="Test output", + output_file="folder/../output.txt" + ) with pytest.raises(ValueError, match="Shell special characters"): - Task(output_file="output.txt | rm -rf /") + Task( + description="Test task", + expected_output="Test output", + output_file="output.txt | rm -rf /" + ) with pytest.raises(ValueError, match="Shell expansion"): - Task(output_file="~/output.txt") + Task( + description="Test task", + expected_output="Test output", + output_file="~/output.txt" + ) with pytest.raises(ValueError, match="Shell expansion"): - Task(output_file="$HOME/output.txt") + Task( + description="Test task", + expected_output="Test output", + output_file="$HOME/output.txt" + ) with pytest.raises(ValueError, match="Invalid template variable"): - Task(output_file="{invalid-name}/output.txt") + Task( + description="Test task", + expected_output="Test output", + output_file="{invalid-name}/output.txt" + )