mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-17 20:38:06 +00:00
Fix validation of target_exe
blob name (#1371)
This commit is contained in:
@ -4,9 +4,8 @@
|
|||||||
# Licensed under the MIT License.
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import ntpath
|
|
||||||
import os
|
import os
|
||||||
import posixpath
|
import pathlib
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
@ -105,14 +104,12 @@ def check_target_exe(config: TaskConfig, definition: TaskDefinition) -> None:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Azure Blob Store uses virtualized directory structures. As such, we need
|
# User-submitted paths must be relative to the setup directory that contains them.
|
||||||
# the paths to already be canonicalized. As an example, accessing the blob
|
# They also must be normalized, and exclude special filesystem path elements.
|
||||||
# store path "./foo" generates an exception, but "foo" and "foo/bar" do
|
#
|
||||||
# not.
|
# For example, accessing the blob store path "./foo" generates an exception, but
|
||||||
if (
|
# "foo" and "foo/bar" do not.
|
||||||
posixpath.relpath(config.task.target_exe) != config.task.target_exe
|
if not is_valid_blob_name(config.task.target_exe):
|
||||||
or ntpath.relpath(config.task.target_exe) != config.task.target_exe
|
|
||||||
):
|
|
||||||
raise TaskConfigError("target_exe must be a canonicalized relative path")
|
raise TaskConfigError("target_exe must be a canonicalized relative path")
|
||||||
|
|
||||||
container = [x for x in config.containers if x.type == ContainerType.setup][0]
|
container = [x for x in config.containers if x.type == ContainerType.setup][0]
|
||||||
@ -124,6 +121,53 @@ def check_target_exe(config: TaskConfig, definition: TaskDefinition) -> None:
|
|||||||
LOGGER.warning(err)
|
LOGGER.warning(err)
|
||||||
|
|
||||||
|
|
||||||
|
# Azure Blob Storage uses a flat scheme, and has no true directory hierarchy. Forward
|
||||||
|
# slashes are used to delimit a _virtual_ directory structure.
|
||||||
|
def is_valid_blob_name(blob_name: str) -> bool:
|
||||||
|
# https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#blob-names
|
||||||
|
MIN_LENGTH = 1
|
||||||
|
MAX_LENGTH = 1024 # Inclusive
|
||||||
|
MAX_PATH_SEGMENTS = 254
|
||||||
|
|
||||||
|
length = len(blob_name)
|
||||||
|
|
||||||
|
# No leading/trailing whitespace.
|
||||||
|
if blob_name != blob_name.strip():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if length < MIN_LENGTH:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if length > MAX_LENGTH:
|
||||||
|
return False
|
||||||
|
|
||||||
|
path = pathlib.PurePosixPath(blob_name)
|
||||||
|
|
||||||
|
if len(path.parts) > MAX_PATH_SEGMENTS:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# No path segment should end with a dot (`.`).
|
||||||
|
for part in path.parts:
|
||||||
|
if part.endswith("."):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Reject absolute paths to avoid confusion.
|
||||||
|
if path.is_absolute():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Reject paths with special relative filesystem entries.
|
||||||
|
if "." in path.parts:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if ".." in path.parts:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Will not have a leading `.`, even if `blob_name` does.
|
||||||
|
normalized = path.as_posix()
|
||||||
|
|
||||||
|
return blob_name == normalized
|
||||||
|
|
||||||
|
|
||||||
def target_uses_input(config: TaskConfig) -> bool:
|
def target_uses_input(config: TaskConfig) -> bool:
|
||||||
if config.task.target_options is not None:
|
if config.task.target_options is not None:
|
||||||
for option in config.task.target_options:
|
for option in config.task.target_options:
|
||||||
|
57
src/api-service/tests/test_task_config.py
Normal file
57
src/api-service/tests/test_task_config.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from __app__.onefuzzlib.tasks.config import is_valid_blob_name
|
||||||
|
|
||||||
|
BlobNameTestCase = Tuple[str, bool]
|
||||||
|
|
||||||
|
|
||||||
|
BLOB_NAME_TEST_CASES = [
|
||||||
|
# Valid
|
||||||
|
("fuzz.exe", True),
|
||||||
|
("bin/fuzz.exe", True),
|
||||||
|
("/".join("a" * 254), True),
|
||||||
|
("a" * 1024, True),
|
||||||
|
# Invalid (absolute)
|
||||||
|
("/fuzz.exe", False),
|
||||||
|
("/bin/fuzz.exe", False),
|
||||||
|
# Invalid (special dirs)
|
||||||
|
("./fuzz.exe", False),
|
||||||
|
("././fuzz.exe", False),
|
||||||
|
("./bin/fuzz.exe", False),
|
||||||
|
("./bin/./fuzz.exe", False),
|
||||||
|
("../fuzz.exe", False),
|
||||||
|
("../bin/fuzz.exe", False),
|
||||||
|
(".././fuzz.exe", False),
|
||||||
|
("../bin/./fuzz.exe", False),
|
||||||
|
# Out of Azure size bounds
|
||||||
|
("", False),
|
||||||
|
(" ", False),
|
||||||
|
("/".join("a" * 255), False),
|
||||||
|
("a" * 1025, False),
|
||||||
|
# Paths with invalid segments.
|
||||||
|
("a.", False),
|
||||||
|
("a..", False),
|
||||||
|
("a./b", False),
|
||||||
|
("a/b./c", False),
|
||||||
|
("a./", False),
|
||||||
|
("a../", False),
|
||||||
|
("a./b/", False),
|
||||||
|
("a/b./c/", False),
|
||||||
|
("a//", False),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("blob_name_test_case", BLOB_NAME_TEST_CASES)
|
||||||
|
def test_is_valid_blob_name(blob_name_test_case: BlobNameTestCase) -> None:
|
||||||
|
blob_name, expected = blob_name_test_case
|
||||||
|
|
||||||
|
is_valid = is_valid_blob_name(blob_name)
|
||||||
|
|
||||||
|
assert is_valid == expected
|
Reference in New Issue
Block a user