Spaces:
Running
Running
| from multi_agent_sdlc.runtime.paths import normalise_relative_path | |
| from multi_agent_sdlc.tools.coder.validation import MODULE_PATTERN | |
| from collections.abc import Callable | |
| from packaging.requirements import InvalidRequirement, Requirement | |
| from packaging.utils import canonicalize_name | |
| def validate_application_arguments( | |
| arguments: list[str], | |
| ) -> list[str]: | |
| """Validate arguments passed to an application or module.""" | |
| validated: list[str] = [] | |
| for argument in arguments: | |
| if not isinstance(argument, str): | |
| raise TypeError("Every application argument must be a string.") | |
| if "\n" in argument or "\r" in argument: | |
| raise PermissionError("Multiline application arguments are not allowed.") | |
| if "\x00" in argument: | |
| raise PermissionError( | |
| "Null bytes are not allowed in application arguments." | |
| ) | |
| validated.append(argument) | |
| return validated | |
| def validate_project_relative_path(path: str) -> str: | |
| candidate = normalise_relative_path(path.strip()) | |
| return candidate.as_posix() | |
| def validate_file_content(content: str) -> str: | |
| if "\x00" in content: | |
| raise ValueError("File content cannot contain null bytes.") | |
| return content | |
| def create_entry_point_validator( | |
| role: str, | |
| prohibited_entry_points: set[str], | |
| ) -> Callable[[str], str]: | |
| def validate_entry_point(entry_point: str) -> str: | |
| cleaned = entry_point.strip() | |
| if not cleaned: | |
| raise ValueError("Entry point cannot be empty.") | |
| if cleaned.casefold() in prohibited_entry_points: | |
| raise ValueError(f"The {role} cannot execute {cleaned}.") | |
| return cleaned | |
| return validate_entry_point | |
| def create_module_name_validator( | |
| role: str, | |
| prohibited_python_modules: set[str], | |
| ) -> Callable[[str], str]: | |
| def validate_module_name(module: str) -> str: | |
| cleaned = module.strip() | |
| if not cleaned: | |
| raise ValueError("Module cannot be empty.") | |
| if not MODULE_PATTERN.fullmatch(cleaned): | |
| raise ValueError(f"Invalid Python module name: {cleaned}") | |
| root_module = cleaned.split(".", maxsplit=1)[0].lower() | |
| if root_module in prohibited_python_modules: | |
| raise PermissionError( | |
| f"The {role} cannot execute Python module `{cleaned}`." | |
| ) | |
| return cleaned | |
| return validate_module_name | |
| def create_testing_dependency_validator( | |
| role: str, | |
| prohibited_tester_dependencies: set[str], | |
| ) -> Callable[[str], str]: | |
| canonical_prohibited_dependencies = set( | |
| canonicalize_name(name) for name in prohibited_tester_dependencies | |
| ) | |
| def validate_testing_dependency( | |
| dependency: str, | |
| ) -> str: | |
| cleaned = dependency.strip() | |
| if not cleaned: | |
| raise ValueError("Dependency specification cannot be empty.") | |
| if cleaned.startswith("-"): | |
| raise PermissionError( | |
| "Dependency command-line options are not allowed: " f"{cleaned!r}." | |
| ) | |
| requirement = parse_dependency(cleaned) | |
| if requirement.url is not None: | |
| raise PermissionError( | |
| "Git, URL, SSH, and local-file dependencies are not " | |
| f"allowed: {cleaned!r}." | |
| ) | |
| dependency_name = canonicalize_name(requirement.name) | |
| if dependency_name in canonical_prohibited_dependencies: | |
| raise PermissionError(f"The {role} cannot add dependency {cleaned!r}.") | |
| return cleaned | |
| return validate_testing_dependency | |
| def parse_dependency( | |
| specification: str, | |
| ) -> Requirement: | |
| """Parse a Python dependency specification.""" | |
| try: | |
| return Requirement(specification) | |
| except InvalidRequirement as error: | |
| raise ValueError( | |
| f"Invalid dependency specification: {specification!r}." | |
| ) from error | |