Skip to content

Commit

Permalink
Merge pull request #364 from SolidifiedRay/issue304
Browse files Browse the repository at this point in the history
Add a new flag to specify where to write the generated metadata
  • Loading branch information
lukpueh committed Jun 26, 2020
2 parents 6f7f5cc + 5840165 commit 6cfa36c
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 9 deletions.
9 changes: 9 additions & 0 deletions in_toto/common_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@
"help": "suppress all output"
}

METADATA_DIRECTORY_ARGS = ["-d", "--metadata-directory"]
METADATA_DIRECTORY_KWARGS = {
"required": False,
"type": str,
"metavar": "<directory>",
"help": ("path to a directory to dump metadata. If '--metadata-directory'"
" is not passed, the current working direcotry is used.")
}


def title_case_action_groups(parser):
"""Capitalize the first character of all words in the title of each action
Expand Down
17 changes: 15 additions & 2 deletions in_toto/in_toto_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
BASE_PATH_KWARGS, LSTRIP_PATHS_ARGS, LSTRIP_PATHS_KWARGS, KEY_ARGS,
KEY_KWARGS, KEY_TYPE_KWARGS, KEY_TYPE_ARGS, GPG_ARGS, GPG_KWARGS,
GPG_HOME_ARGS, GPG_HOME_KWARGS, VERBOSE_ARGS, VERBOSE_KWARGS, QUIET_ARGS,
QUIET_KWARGS, sort_action_groups, title_case_action_groups)
QUIET_KWARGS, METADATA_DIRECTORY_ARGS, METADATA_DIRECTORY_KWARGS,
sort_action_groups, title_case_action_groups)


# Command line interfaces should use in_toto base logger (c.f. in_toto.log)
Expand Down Expand Up @@ -68,6 +69,14 @@ def create_parser():
{prog} start -n edit-foo --gpg -m path/to/foo
{prog} stop -n edit-foo --gpg -p path/to/foo
Create link metadata file signed with the private key loaded from 'key_file',
record all files in the CWD as material and product, and dump finished link
file to the target directory (on stop).
{prog} start -n edit-files -k path/to/key_file -m .
{prog} stop -d path/to/target/dir -n edit-files -k path/to/key_file -p .
""".format(prog=parser.prog)

# The subparsers inherit the arguments from the parent parser
Expand Down Expand Up @@ -128,6 +137,9 @@ def create_parser():
" resulting link metadata's product section when running the 'stop'"
" subcommand. Symlinks to files are followed."))

subparser_stop.add_argument(*METADATA_DIRECTORY_ARGS,
**METADATA_DIRECTORY_KWARGS)

parser.add_argument('--version', action='version',
version='{} {}'.format(parser.prog, __version__))

Expand Down Expand Up @@ -187,7 +199,8 @@ def main():
signing_key=key, gpg_keyid=gpg_keyid,
gpg_use_default=gpg_use_default, gpg_home=args.gpg_home,
exclude_patterns=args.exclude_patterns, base_path=args.base_path,
lstrip_paths=args.lstrip_paths)
lstrip_paths=args.lstrip_paths,
metadata_directory=args.metadata_directory)

except Exception as e:
LOG.error("(in-toto-record {0}) {1}: {2}"
Expand Down
7 changes: 5 additions & 2 deletions in_toto/in_toto_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
BASE_PATH_KWARGS, LSTRIP_PATHS_ARGS, LSTRIP_PATHS_KWARGS, KEY_ARGS,
KEY_KWARGS, KEY_TYPE_KWARGS, KEY_TYPE_ARGS, GPG_ARGS, GPG_KWARGS,
GPG_HOME_ARGS, GPG_HOME_KWARGS, VERBOSE_ARGS, VERBOSE_KWARGS, QUIET_ARGS,
QUIET_KWARGS, sort_action_groups, title_case_action_groups)
QUIET_KWARGS, METADATA_DIRECTORY_ARGS, METADATA_DIRECTORY_KWARGS,
sort_action_groups, title_case_action_groups)

# Command line interfaces should use in_toto base logger (c.f. in_toto.log)
LOG = logging.getLogger("in_toto")
Expand Down Expand Up @@ -141,6 +142,7 @@ def create_parser():
parser.add_argument(*EXCLUDE_ARGS, **EXCLUDE_KWARGS)
parser.add_argument(*BASE_PATH_ARGS, **BASE_PATH_KWARGS)
parser.add_argument(*LSTRIP_PATHS_ARGS, **LSTRIP_PATHS_KWARGS)
parser.add_argument(*METADATA_DIRECTORY_ARGS, **METADATA_DIRECTORY_KWARGS)

verbosity_args = parser.add_mutually_exclusive_group(required=False)
verbosity_args.add_argument(*VERBOSE_ARGS, **VERBOSE_KWARGS)
Expand Down Expand Up @@ -209,7 +211,8 @@ def main():
record_streams=args.record_streams, signing_key=key,
gpg_keyid=gpg_keyid, gpg_use_default=gpg_use_default,
gpg_home=args.gpg_home, exclude_patterns=args.exclude_patterns,
base_path=args.base_path, lstrip_paths=args.lstrip_paths)
base_path=args.base_path, lstrip_paths=args.lstrip_paths,
metadata_directory=args.metadata_directory)

except Exception as e:
LOG.error("(in-toto-run) {0}: {1}".format(type(e).__name__, e))
Expand Down
33 changes: 28 additions & 5 deletions in_toto/runlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
record_streams=False, signing_key=None, gpg_keyid=None,
gpg_use_default=False, gpg_home=None, exclude_patterns=None,
base_path=None, compact_json=False, record_environment=False,
normalize_line_endings=False, lstrip_paths=None):
normalize_line_endings=False, lstrip_paths=None, metadata_directory=None):
"""Performs a supply chain step or inspection generating link metadata.
Executes link_cmd_args, recording paths and hashes of files before and after
Expand Down Expand Up @@ -482,6 +482,9 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
lstrip_paths (optional): A list of path prefixes used to left-strip
artifact paths before storing them in the resulting link metadata.
metadata_directory (optional): A directory path to write the resulting link
metadata file to. Default destination is the current working directory.
Raises:
securesystemslib.exceptions.FormatError: Passed arguments are malformed.
Expand All @@ -493,7 +496,8 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
securesystemslib.process.subprocess.TimeoutExpired: Link command times out.
IOError: Cannot write link metadata.
IOError, FileNotFoundError, NotADirectoryError, PermissionError:
Cannot write link metadata.
securesystemslib.exceptions.CryptoError, \
securesystemslib.exceptions.UnsupportedAlgorithmError:
Expand Down Expand Up @@ -527,6 +531,9 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
if base_path:
securesystemslib.formats.PATH_SCHEMA.check_match(base_path)

if metadata_directory:
securesystemslib.formats.PATH_SCHEMA.check_match(metadata_directory)

if material_list:
LOG.info("Recording materials '{}'...".format(", ".join(material_list)))

Expand Down Expand Up @@ -578,6 +585,10 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
if signature:
signing_keyid = signature["keyid"]
filename = FILENAME_FORMAT.format(step_name=name, keyid=signing_keyid)

if metadata_directory is not None:
filename = os.path.join(metadata_directory, filename)

LOG.info("Storing link metadata to '{}'...".format(filename))
link_metadata.dump(filename)

Expand Down Expand Up @@ -647,7 +658,8 @@ def in_toto_record_start(step_name, material_list, signing_key=None,
securesystemslib.process.subprocess.TimeoutExpired: Link command times out.
IOError: Cannot write link metadata.
IOError, PermissionError:
Cannot write link metadata.
securesystemslib.exceptions.CryptoError, \
securesystemslib.exceptions.UnsupportedAlgorithmError:
Expand Down Expand Up @@ -728,7 +740,7 @@ def in_toto_record_start(step_name, material_list, signing_key=None,
def in_toto_record_stop(step_name, product_list, signing_key=None,
gpg_keyid=None, gpg_use_default=False, gpg_home=None,
exclude_patterns=None, base_path=None, normalize_line_endings=False,
lstrip_paths=None):
lstrip_paths=None, metadata_directory=None):
"""Finalizes preliminary link metadata generated with in_toto_record_start.
Loads preliminary link metadata file, verifies its signature, and records
Expand Down Expand Up @@ -774,6 +786,9 @@ def in_toto_record_stop(step_name, product_list, signing_key=None,
lstrip_paths (optional): A list of path prefixes used to left-strip
artifact paths before storing them in the resulting link metadata.
metadata_directory (optional): A directory path to write the resulting link
metadata file to. Default destination is the current working directory.
Raises:
securesystemslib.exceptions.FormatError: Passed arguments are malformed.
Expand All @@ -788,7 +803,8 @@ def in_toto_record_stop(step_name, product_list, signing_key=None,
securesystemslib.process.subprocess.TimeoutExpired: Link command times out.
IOError: Cannot write link metadata.
IOError, FileNotFoundError, NotADirectoryError, PermissionError:
Cannot write link metadata.
securesystemslib.exceptions.CryptoError, \
securesystemslib.exceptions.UnsupportedAlgorithmError:
Expand Down Expand Up @@ -824,6 +840,9 @@ def in_toto_record_stop(step_name, product_list, signing_key=None,
if base_path:
securesystemslib.formats.PATH_SCHEMA.check_match(base_path)

if metadata_directory:
securesystemslib.formats.PATH_SCHEMA.check_match(metadata_directory)

# Load preliminary link file
# If we have a signing key we can use the keyid to construct the name
if signing_key:
Expand Down Expand Up @@ -904,6 +923,10 @@ def in_toto_record_stop(step_name, product_list, signing_key=None,
link_metadata.sign_gpg(keyid, gpg_home)

fn = FILENAME_FORMAT.format(step_name=step_name, keyid=keyid)

if metadata_directory is not None:
fn = os.path.join(metadata_directory, fn)

LOG.info("Storing link metadata to '{}'...".format(fn))
link_metadata.dump(fn)

Expand Down
8 changes: 8 additions & 0 deletions tests/test_in_toto_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import sys
import unittest
import tempfile
import os

if sys.version_info >= (3, 3):
import unittest.mock as mock # pylint: disable=no-name-in-module,import-error
Expand Down Expand Up @@ -145,6 +147,12 @@ def test_start_stop(self):
self.assert_cli_sys_exit(["start"] + args, 0)
self.assert_cli_sys_exit(["stop"] + args, 0)

# Start/stop sign with metadata directory
args = ["--step-name", "test9", "--key", self.rsa_key_path]
tmp_dir = os.path.realpath(tempfile.mkdtemp(dir=os.getcwd()))
metadata_directory_arg = ["--metadata-directory", tmp_dir]
self.assert_cli_sys_exit(["start"] + args, 0)
self.assert_cli_sys_exit(["stop"] + metadata_directory_arg + args, 0)


def test_glob_no_unfinished_files(self):
Expand Down
16 changes: 16 additions & 0 deletions tests/test_in_toto_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import sys
import unittest
import glob
import tempfile

# Use external backport 'mock' on versions under 3.3
if sys.version_info >= (3, 3):
Expand Down Expand Up @@ -140,6 +141,21 @@ def test_main_optional_args(self):
[self.test_artifact[len(strip_prefix):]])


def test_main_with_metadata_directory(self):
"""Test CLI command with metadata directory. """
tmp_dir = os.path.realpath(tempfile.mkdtemp(dir=os.getcwd()))
args = ["--step-name", self.test_step, "--key", self.rsa_key_path,
"--metadata-directory", tmp_dir, "--", "python", "--version"]

# Give wrong password whenever prompted.
with mock.patch('in_toto.util.prompt_password', return_value='x'):
self.assert_cli_sys_exit(args, 0)

linkpath = os.path.join(tmp_dir, self.test_link_rsa)

self.assertTrue(os.path.exists(linkpath))


def test_main_with_unencrypted_ed25519_key(self):
"""Test CLI command with ed25519 key. """
args = ["-n", self.test_step,
Expand Down
Loading

0 comments on commit 6cfa36c

Please sign in to comment.