|
58 | 58 | updater.download_target(targetinfo, "~/tufclient/downloads/") |
59 | 59 | """ |
60 | 60 |
|
61 | | -import fnmatch |
62 | 61 | import logging |
63 | 62 | import os |
64 | 63 | from typing import Any, Dict, List, Optional |
65 | 64 | from urllib import parse |
66 | 65 |
|
67 | | -from securesystemslib import hash as sslib_hash |
68 | 66 | from securesystemslib import util as sslib_util |
69 | 67 |
|
70 | 68 | from tuf import exceptions |
@@ -414,8 +412,8 @@ def _preorder_depth_first_walk(self, target_filepath) -> Dict: |
414 | 412 | # NOTE: This may be a slow operation if there are many |
415 | 413 | # delegated roles. |
416 | 414 | for child_role in child_roles: |
417 | | - child_role_name = _visit_child_role( |
418 | | - child_role, target_filepath |
| 415 | + child_role_name = child_role.visit_child_role( |
| 416 | + target_filepath |
419 | 417 | ) |
420 | 418 |
|
421 | 419 | if child_role.terminating and child_role_name is not None: |
@@ -463,117 +461,6 @@ def _preorder_depth_first_walk(self, target_filepath) -> Dict: |
463 | 461 | return {"filepath": target_filepath, "fileinfo": target} |
464 | 462 |
|
465 | 463 |
|
466 | | -def _visit_child_role(child_role: Dict, target_filepath: str) -> str: |
467 | | - """ |
468 | | - <Purpose> |
469 | | - Non-public method that determines whether the given 'target_filepath' |
470 | | - is an allowed path of 'child_role'. |
471 | | -
|
472 | | - Ensure that we explore only delegated roles trusted with the target. The |
473 | | - metadata for 'child_role' should have been refreshed prior to this point, |
474 | | - however, the paths/targets that 'child_role' signs for have not been |
475 | | - verified (as intended). The paths/targets that 'child_role' is allowed |
476 | | - to specify in its metadata depends on the delegating role, and thus is |
477 | | - left to the caller to verify. We verify here that 'target_filepath' |
478 | | - is an allowed path according to the delegated 'child_role'. |
479 | | -
|
480 | | - TODO: Should the TUF spec restrict the repository to one particular |
481 | | - algorithm? Should we allow the repository to specify in the role |
482 | | - dictionary the algorithm used for these generated hashed paths? |
483 | | -
|
484 | | - <Arguments> |
485 | | - child_role: |
486 | | - The delegation targets role object of 'child_role', containing its |
487 | | - paths, path_hash_prefixes, keys, and so on. |
488 | | -
|
489 | | - target_filepath: |
490 | | - The path to the target file on the repository. This will be relative to |
491 | | - the 'targets' (or equivalent) directory on a given mirror. |
492 | | -
|
493 | | - <Exceptions> |
494 | | - None. |
495 | | -
|
496 | | - <Side Effects> |
497 | | - None. |
498 | | -
|
499 | | - <Returns> |
500 | | - If 'child_role' has been delegated the target with the name |
501 | | - 'target_filepath', then we return the role name of 'child_role'. |
502 | | -
|
503 | | - Otherwise, we return None. |
504 | | - """ |
505 | | - |
506 | | - child_role_name = child_role.name |
507 | | - child_role_paths = child_role.paths |
508 | | - child_role_path_hash_prefixes = child_role.path_hash_prefixes |
509 | | - |
510 | | - if child_role_path_hash_prefixes is not None: |
511 | | - target_filepath_hash = _get_filepath_hash(target_filepath) |
512 | | - for child_role_path_hash_prefix in child_role_path_hash_prefixes: |
513 | | - if not target_filepath_hash.startswith(child_role_path_hash_prefix): |
514 | | - continue |
515 | | - |
516 | | - return child_role_name |
517 | | - |
518 | | - elif child_role_paths is not None: |
519 | | - # Is 'child_role_name' allowed to sign for 'target_filepath'? |
520 | | - for child_role_path in child_role_paths: |
521 | | - # A child role path may be an explicit path or glob pattern (Unix |
522 | | - # shell-style wildcards). The child role 'child_role_name' is |
523 | | - # returned if 'target_filepath' is equal to or matches |
524 | | - # 'child_role_path'. Explicit filepaths are also considered |
525 | | - # matches. A repo maintainer might delegate a glob pattern with a |
526 | | - # leading path separator, while the client requests a matching |
527 | | - # target without a leading path separator - make sure to strip any |
528 | | - # leading path separators so that a match is made. |
529 | | - # Example: "foo.tgz" should match with "/*.tgz". |
530 | | - if fnmatch.fnmatch( |
531 | | - target_filepath.lstrip(os.sep), child_role_path.lstrip(os.sep) |
532 | | - ): |
533 | | - logger.debug( |
534 | | - "Child role %s is allowed to sign for %s", |
535 | | - repr(child_role_name), |
536 | | - repr(target_filepath), |
537 | | - ) |
538 | | - |
539 | | - return child_role_name |
540 | | - |
541 | | - logger.debug( |
542 | | - "The given target path %s " |
543 | | - "does not match the trusted path or glob pattern: %s", |
544 | | - repr(target_filepath), |
545 | | - repr(child_role_path), |
546 | | - ) |
547 | | - continue |
548 | | - |
549 | | - else: |
550 | | - # 'role_name' should have been validated when it was downloaded. |
551 | | - # The 'paths' or 'path_hash_prefixes' fields should not be missing, |
552 | | - # so we raise a format error here in case they are both missing. |
553 | | - raise exceptions.FormatError( |
554 | | - repr(child_role_name) + " " |
555 | | - 'has neither a "paths" nor "path_hash_prefixes". At least' |
556 | | - " one of these attributes must be present." |
557 | | - ) |
558 | | - |
559 | | - return None |
560 | | - |
561 | | - |
562 | | -def _get_filepath_hash(target_filepath, hash_function="sha256"): |
563 | | - """ |
564 | | - Calculate the hash of the filepath to determine which bin to find the |
565 | | - target. |
566 | | - """ |
567 | | - # The client currently assumes the repository (i.e., repository |
568 | | - # tool) uses 'hash_function' to generate hashes and UTF-8. |
569 | | - digest_object = sslib_hash.digest(hash_function) |
570 | | - encoded_target_filepath = target_filepath.encode("utf-8") |
571 | | - digest_object.update(encoded_target_filepath) |
572 | | - target_filepath_hash = digest_object.hexdigest() |
573 | | - |
574 | | - return target_filepath_hash |
575 | | - |
576 | | - |
577 | 464 | def _ensure_trailing_slash(url: str): |
578 | 465 | """Return url guaranteed to end in a slash""" |
579 | 466 | return url if url.endswith("/") else f"{url}/" |
0 commit comments