Skip to content

Commit

Permalink
Merge f9da287 into 8482f2c
Browse files Browse the repository at this point in the history
  • Loading branch information
MVrachev committed Aug 26, 2021
2 parents 8482f2c + f9da287 commit 6189b3f
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 11 deletions.
34 changes: 32 additions & 2 deletions tests/test_api.py
Expand Up @@ -31,7 +31,7 @@
MetaFile,
TargetFile,
Delegations,
DelegatedRole,
DelegatedRole
)

from tuf.api.serialization import (
Expand All @@ -40,7 +40,6 @@

from tuf.api.serialization.json import (
JSONSerializer,
JSONDeserializer,
CanonicalJSONSerializer
)

Expand Down Expand Up @@ -465,6 +464,37 @@ def test_metadata_root(self):
with self.assertRaises(KeyError):
root.signed.remove_key('root', 'nosuchkey')

def test_is_target_in_pathpattern(self):
supported_use_cases = [
("foo.tgz", "foo.tgz"),
("foo.tgz", "*"),
("foo.tgz", "*.tgz"),
("foo-version-a.tgz", "foo-version-?.tgz"),
("targets/foo.tgz", "targets/*.tgz"),
("foo/bar/zoo/k.tgz", "foo/bar/zoo/*"),
("foo/bar/zoo/k.tgz", "foo/*/zoo/*"),
("foo/bar/zoo/k.tgz", "*/*/*/*"),
("foo/bar", "f?o/bar"),
("foo/bar", "*o/bar"),
]
for targetpath, pathpattern in supported_use_cases:
self.assertTrue(
DelegatedRole._is_target_in_pathpattern(targetpath, pathpattern)
)

invalid_use_cases = [
("targets/foo.tgz", "*.tgz"),
("/foo.tgz", "*.tgz",),
("targets/foo.tgz", "*"),
("foo-version-alpha.tgz", "foo-version-?.tgz"),
("foo//bar", "*/bar"),
("foo/bar", "f?/bar")
]
for targetpath, pathpattern in invalid_use_cases:
self.assertFalse(
DelegatedRole._is_target_in_pathpattern(targetpath, pathpattern)
)


def test_delegation_class(self):
# empty keys and roles
Expand Down
35 changes: 26 additions & 9 deletions tuf/api/metadata.py
Expand Up @@ -19,7 +19,6 @@
import fnmatch
import io
import logging
import os
import tempfile
from collections import OrderedDict
from datetime import datetime, timedelta
Expand Down Expand Up @@ -1057,9 +1056,32 @@ def to_dict(self) -> Dict[str, Any]:
res_dict["path_hash_prefixes"] = self.path_hash_prefixes
return res_dict

@staticmethod
def _is_target_in_pathpattern(targetpath: str, pathpattern: str) -> bool:
"""Determines whether "targetname" matches the "pathpattern"."""
# We need to make sure that targetname and pathpattern are pointing to
# the same directory as fnmatch doesn't threat "/" as a special symbol.
target_parts = targetpath.split("/")
pattern_parts = pathpattern.split("/")
if len(target_parts) != len(pattern_parts):
return False

# Every part in the pathpattern could include a glob pattern, that's why
# each of the target and pathpattern parts should match.
for target_dir, pattern_dir in zip(target_parts, pattern_parts):
if not fnmatch.fnmatch(target_dir, pattern_dir):
return False

return True

def is_delegated_path(self, target_filepath: str) -> bool:
"""Determines whether the given 'target_filepath' is in one of
the paths that DelegatedRole is trusted to provide"""
the paths that DelegatedRole is trusted to provide.
Note: we only support "/" as a separator and don't handle corner cases
such as leading separators in pathpattern or target_filepath. See:
https://theupdateframework.github.io/specification/latest/#targetpath
"""

if self.path_hash_prefixes is not None:
# Calculate the hash of the filepath
Expand All @@ -1075,13 +1097,8 @@ def is_delegated_path(self, target_filepath: str) -> bool:
elif self.paths is not None:
for pathpattern in self.paths:
# A delegated role path may be an explicit path or glob
# pattern (Unix shell-style wildcards). Explicit filepaths
# are also considered matches. Make sure to strip any leading
# path separators so that a match is made.
# Example: "foo.tgz" should match with "/*.tgz".
if fnmatch.fnmatch(
target_filepath.lstrip(os.sep), pathpattern.lstrip(os.sep)
):
# pattern (Unix shell-style wildcards).
if self._is_target_in_pathpattern(target_filepath, pathpattern):
return True

return False
Expand Down

0 comments on commit 6189b3f

Please sign in to comment.