Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/api/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在python中清理文件路径_Python - Fatal编程技术网

在python中清理文件路径

在python中清理文件路径,python,Python,我有一个文件浏览器应用程序,它向用户公开目录及其内容 我想清理用户输入,这是一个文件路径,因此它不允许绝对路径,如“/tmp/”和相对路径,如“../../etc” 是否有跨平台执行此操作的python函数?针对python的全面文件路径清理器 我对清除路径的任何可用方法都不太满意,所以我编写了自己的、相对全面的路径消毒剂。这适用于*从公共端点(http上载、REST端点等)获取输入,并确保如果在生成的文件路径上保存数据,不会损坏系统**。(注意:这段代码的目标是Python3+,您可能需要做一

我有一个文件浏览器应用程序,它向用户公开目录及其内容

我想清理用户输入,这是一个文件路径,因此它不允许绝对路径,如“/tmp/”和相对路径,如“../../etc”

是否有跨平台执行此操作的python函数?

针对python的全面文件路径清理器 我对清除路径的任何可用方法都不太满意,所以我编写了自己的、相对全面的路径消毒剂。这适用于*从公共端点(http上载、REST端点等)获取输入,并确保如果在生成的文件路径上保存数据,不会损坏系统**。(注意:这段代码的目标是Python3+,您可能需要做一些更改才能使其在2.x上工作)

*不保证!请不要依赖此代码,除非您自己彻底检查它

**再说一次,没有保证!您仍然可以做一些疯狂的事情,将*nix系统上的根路径设置为
/dev/
/bin/
之类的。不要那样做。Windows上也存在一些可能导致损坏的边缘情况(例如,设备文件名),如果您以Windows为目标,您可以从
werkzeug
utils
中检查
secure\u filename
方法,以获得处理这些问题的良好开端

工作原理
  • 您需要指定一个根路径,消毒剂将确保返回的所有路径都在该根路径下。检查
    get\u root\u path
    函数以了解执行此操作的位置。确保根路径的值来自您自己的配置,而不是来自用户的输入
  • 有一个文件名sanitiser,它:
    • 将unicode转换为ASCII
    • 将路径分隔符转换为下划线
    • 仅允许文件名中白名单中的某些字符。白名单包括所有小写和大写字母、所有数字、连字符、下划线、空格、左括号和右括号以及句号(句号)。如果愿意,您可以自定义此白名单
    • 确保所有名称至少有一个字母或数字(以避免使用像“..”这样的名称)
  • 要获取有效的文件路径,应调用
    make\u valid\u file\u path
    。您可以选择在
    path
    参数中向其传递一个子目录路径。这是根路径下的路径,可以来自用户输入。您可以选择在
    filename
    参数中向其传递文件名,这也可以来自用户输入。您传递的文件名中的任何路径信息都不会用于确定文件的路径,而是将其展平为文件名的有效、安全组件。
    • 如果没有路径或文件名,它将返回根路径,该路径的格式对于主机文件系统是正确的,并且带有一个尾随路径分隔符(/)
    • 如果存在子目录路径,它会将其拆分为其组成部分,使用文件名sanitiser对每个部分进行清理,并在不使用前导路径分隔符的情况下重建路径
    • 如果有一个文件名,它将使用消毒剂对该文件名进行消毒
    • 它将
      os.path.join
      路径组件以获得文件的最终路径
    • 作为对结果路径是否有效和安全的最后双重检查,它将检查结果路径是否位于根路径下的某个位置。此检查通过拆分和比较路径的组成部分来正确完成,而不仅仅是确保一个字符串以另一个字符串开头
好了,足够的警告和说明,下面是代码:

import os

def ensure_directory_exists(path_directory):
    if not os.path.exists(path_directory):
        os.makedirs(path_directory)

def os_path_separators():
    seps = []
    for sep in os.path.sep, os.path.altsep:
        if sep:
            seps.append(sep)
    return seps

def sanitise_filesystem_name(potential_file_path_name):
    # Sort out unicode characters
    valid_filename = normalize('NFKD', potential_file_path_name).encode('ascii', 'ignore').decode('ascii')
    # Replace path separators with underscores
    for sep in os_path_separators():
        valid_filename = valid_filename.replace(sep, '_')
    # Ensure only valid characters
    valid_chars = "-_.() {0}{1}".format(string.ascii_letters, string.digits)
    valid_filename = "".join(ch for ch in valid_filename if ch in valid_chars)
    # Ensure at least one letter or number to ignore names such as '..'
    valid_chars = "{0}{1}".format(string.ascii_letters, string.digits)
    test_filename = "".join(ch for ch in potential_file_path_name if ch in valid_chars)
    if len(test_filename) == 0:
        # Replace empty file name or file path part with the following
        valid_filename = "(Empty Name)"
    return valid_filename

def get_root_path():
    # Replace with your own root file path, e.g. '/place/to/save/files/'
    filepath = get_file_root_from_config()
    filepath = os.path.abspath(filepath)
    # ensure trailing path separator (/)
    if not any(filepath[-1] == sep for sep in os_path_separators()):
        filepath = '{0}{1}'.format(filepath, os.path.sep)
    ensure_directory_exists(filepath)
    return filepath

def path_split_into_list(path):
    # Gets all parts of the path as a list, excluding path separators
    parts = []
    while True:
        newpath, tail = os.path.split(path)
        if newpath == path:
            assert not tail
            if path and path not in os_path_separators():
                parts.append(path)
            break
        if tail and tail not in os_path_separators():
            parts.append(tail)
        path = newpath
    parts.reverse()
    return parts

def sanitise_filesystem_path(potential_file_path):
    # Splits up a path and sanitises the name of each part separately
    path_parts_list = path_split_into_list(potential_file_path)
    sanitised_path = ''
    for path_component in path_parts_list:
        sanitised_path = '{0}{1}{2}'.format(sanitised_path, sanitise_filesystem_name(path_component), os.path.sep)
    return sanitised_path

def check_if_path_is_under(parent_path, child_path):
    # Using the function to split paths into lists of component parts, check that one path is underneath another
    child_parts = path_split_into_list(child_path)
    parent_parts = path_split_into_list(parent_path)
    if len(parent_parts) > len(child_parts):
        return False
    return all(part1==part2 for part1, part2 in zip(child_parts, parent_parts))

def make_valid_file_path(path=None, filename=None):
    root_path = get_root_path()
    if path:
        sanitised_path = sanitise_filesystem_path(path)
        if filename:
            sanitised_filename = sanitise_filesystem_name(filename)
            complete_path = os.path.join(root_path, sanitised_path, sanitised_filename)
        else:
            complete_path = os.path.join(root_path, sanitised_path)
    else:
        if filename:
            sanitised_filename = sanitise_filesystem_name(filename)
            complete_path = os.path.join(root_path, sanitised_filename)
        else:
            complete_path = complete_path
    complete_path = os.path.abspath(complete_path)
    if check_if_path_is_under(root_path, complete_path):
        return complete_path
    else:
        return None

也适用于正在寻找摆脱路径中的“a//B”->“a/B”和“a/B//C”->“a/C”的方法的人。
您可以使用。

这将阻止用户输入文件名,如
。/../../../../etc/shadow
,但也不允许在
basedir
下面的子目录中输入文件(即
basedir/subdir/moredir
被阻止):

从pathlib导入路径
测试路径=(路径(basedir)/用户输入.resolve()
如果test_path.parent!=路径(basedir).resolve():
引发异常(f“文件名{test_path}不在{path(basedir)}目录中”)
如果要允许下面的子目录
basedir

if not Path(basedir).resolve()在test_Path.resolve()中。父项:
引发异常(f“文件名{test_path}不在{path(basedir)}目录中”)

我最终在这里寻找一种快速方法来处理我的用例,并最终编写了我自己的。我需要的是一种方法来获取路径并强制它位于CWD中。这适用于处理装载文件的CI系统

def relative_path(the_path: str) -> str:
    '''
    Force the spec path to be relative to the CI workspace
    Sandboxes the path so that you can't escape out of CWD
    '''
    # Make the path absolute
    the_path = os.path.abspath(the_path)
    # If it started with a . it'll now be /${PWD}/
    # We'll get the path relative to cwd
    if the_path.startswith(os.getcwd()):
        the_path = '{}{}'.format(os.sep, os.path.relpath(the_path))
    # Prepend the path with . and it'll now be ./the/path
    the_path = '.{}'.format(the_path)
    return the_path
在我的例子中,我不想引发异常。我只想强制任何给定的路径都将成为CWD中的绝对路径

测试:

def test_relative_path():
    assert relative_path('../test') == './test'
    assert relative_path('../../test') == './test'
    assert relative_path('../../abc/../test') == './test'
    assert relative_path('../../abc/../test/fixtures') == './test/fixtures'
    assert relative_path('../../abc/../.test/fixtures') == './.test/fixtures'
    assert relative_path('/test/foo') == './test/foo'
    assert relative_path('./test/bar') == './test/bar'
    assert relative_path('.test/baz') == './.test/baz'
    assert relative_path('qux') == './qux'

这是@mneil解决方案的改进,使用了
relpath
的第二个秘密参数:

import os.path

def sanitize_path(path):
    """
    Sanitize a path against directory traversals

    >>> sanitize_path('../test')
    'test'
    >>> sanitize_path('../../test')
    'test'
    >>> sanitize_path('../../abc/../test')
    'test'
    >>> sanitize_path('../../abc/../test/fixtures')
    'test/fixtures'
    >>> sanitize_path('../../abc/../.test/fixtures')
    '.test/fixtures'
    >>> sanitize_path('/test/foo')
    'test/foo'
    >>> sanitize_path('./test/bar')
    'test/bar'
    >>> sanitize_path('.test/baz')
    '.test/baz'
    >>> sanitize_path('qux')
    'qux'
    """
    # - pretending to chroot to the current directory
    # - cancelling all redundant paths (/.. = /)
    # - making the path relative
    return os.path.relpath(os.path.normpath(os.path.join("/", path)), "/")

if __name__ == '__main__':
    import doctest
    doctest.testmod()

我认为这应该与浏览器用于生成相对于基的URL的算法相同,您可以在JavaScript中利用该算法来实现相同的功能:
newURL(“../../abc/./test”https://example.com“”。路径名。子字符串(1)==“测试”“
啊,事实证明你不需要
normpath
,因为
relpath
隐式地实现了这一点,但这样做更清晰。