diff options
author | 2022-11-22 14:13:35 -0500 | |
---|---|---|
committer | 2022-11-22 14:13:35 -0500 | |
commit | a5e18d14bb0133bf5a189f7f99adc777a56c9689 (patch) | |
tree | 6bbc1e21ab61a1c62f34b59c17b2c015f7b298d2 | |
parent | Move parse_files to memfs (diff) |
Add safe_path to handle path checks
-rw-r--r-- | snekbox/api/resources/eval.py | 8 | ||||
-rw-r--r-- | snekbox/nsjail.py | 2 | ||||
-rw-r--r-- | snekbox/process.py | 4 | ||||
-rw-r--r-- | snekbox/snekio.py | 36 |
4 files changed, 32 insertions, 18 deletions
diff --git a/snekbox/api/resources/eval.py b/snekbox/api/resources/eval.py index d6549ab..14fa295 100644 --- a/snekbox/api/resources/eval.py +++ b/snekbox/api/resources/eval.py @@ -9,7 +9,7 @@ from snekbox.nsjail import NsJail __all__ = ("EvalResource",) -from snekbox.snekio import FileAttachment, ParsingError +from snekbox.snekio import FileAttachment, IllegalPathError log = logging.getLogger(__name__) @@ -106,8 +106,10 @@ class EvalResource: py_args=req.media["args"], files=[FileAttachment.from_dict(file) for file in req.media.get("files", [])], ) - except ParsingError as e: - raise falcon.HTTPBadRequest(description=f"Invalid file in request: {e}") + except IllegalPathError as e: + raise falcon.HTTPBadRequest( + title="Request file path failed validation", description=str(e) + ) except Exception: log.exception("An exception occurred while trying to process the request") raise falcon.HTTPInternalServerError diff --git a/snekbox/nsjail.py b/snekbox/nsjail.py index ee15f71..30198e5 100644 --- a/snekbox/nsjail.py +++ b/snekbox/nsjail.py @@ -260,4 +260,4 @@ class NsJail: log.info(f"nsjail return code: {returncode}") - return EvalResult(args, returncode, output, attachments=attachments) + return EvalResult(args, returncode, output, files=attachments) diff --git a/snekbox/process.py b/snekbox/process.py index 6740909..552b91a 100644 --- a/snekbox/process.py +++ b/snekbox/process.py @@ -25,8 +25,8 @@ class EvalResult(CompletedProcess[_T]): returncode: int | None, stdout: _T | None = None, stderr: _T | None = None, - attachments: list[FileAttachment] | None = None, + files: list[FileAttachment] | None = None, ) -> None: """Create an evaluation result.""" super().__init__(args, returncode, stdout, stderr) - self.files: list[FileAttachment] = attachments or [] + self.files: list[FileAttachment] = files or [] diff --git a/snekbox/snekio.py b/snekbox/snekio.py index 9f2c3ca..bc71501 100644 --- a/snekbox/snekio.py +++ b/snekbox/snekio.py @@ -9,6 +9,27 @@ from typing import Generic, TypeVar T = TypeVar("T", str, bytes) +def safe_path(path: str) -> str: + """ + Returns the `path` str if there are no security issues. + + Raises: + IllegalPathError: Raised on any path rule violation. + """ + # Disallow absolute paths + if Path(path).is_absolute(): + raise IllegalPathError(f"File path '{path}' must be relative") + + # Disallow traversal beyond root + try: + test_root = Path("/home") + Path(test_root).joinpath(path).resolve().relative_to(test_root.resolve()) + except ValueError: + raise IllegalPathError(f"File path '{path}' may not traverse beyond root") + + return path + + class AttachmentError(ValueError): """Raised when an attachment is invalid.""" @@ -17,7 +38,7 @@ class ParsingError(AttachmentError): """Raised when an incoming file cannot be parsed.""" -class IllegalPathError(AttachmentError): +class IllegalPathError(ParsingError): """Raised when an attachment has an illegal path.""" @@ -31,18 +52,9 @@ class FileAttachment(Generic[T]): @classmethod def from_dict(cls, data: dict[str, str]) -> FileAttachment[bytes]: """Convert a dict to an attachment.""" - name = data["name"] - path = Path(name) - parts = path.parts - - if path.is_absolute() or set(parts[0]) & {"\\", "/"}: - raise IllegalPathError(f"File path '{name}' must be relative") - - if any(set(part) == {"."} for part in parts): - raise IllegalPathError(f"File path '{name}' may not use traversal ('..')") - + path = safe_path(data["path"]) content = b64decode(data.get("content", "")) - return cls(name, content) + return cls(path, content) @classmethod def from_path(cls, file: Path) -> FileAttachment[bytes]: |