dangerzone/dangerzone/errors.py
Alexis Métaireau f04afde8fd
Download and verify cosign signatures
Signatures are stored in the OCI Manifest v2 registry [0], and are
expected to follow the Cosign Signature Specification [0]

The following CLI utilities are provided with `dangerzone-image`:

For checking new container images, upgrading them and downloading them:

- `upgrade` allows to upgrade the current installed image to the
  last one available on the OCI registry, downloading and storing the
  signatures in the process.
- `verify-local` allows the verify the currently installed image against
  downloaded signatures and public key.

To prepare and install archives on air-gapped environments:

- `prepare-archive` helps to prepare an archive to install on another
  machine
- `load-archive` helps upgrade the local image to the archive given
  in argument.

Signatures are stored locally using the format provided by `cosign
download signature`, and the Rekor log index is used to ensure the
requested-to-install container image is fresher than the one already
present on the system.

[0] https://github.com/sigstore/cosign/blob/main/specs/SIGNATURE_SPEC.md
2025-04-29 14:58:52 +02:00

158 lines
4.5 KiB
Python

import functools
import logging
import sys
from typing import Any, Callable, TypeVar, cast
import click
F = TypeVar("F", bound=Callable[..., Any])
log = logging.getLogger(__name__)
class DocumentFilenameException(Exception):
"""Exception for document-related filename errors."""
class AddedDuplicateDocumentException(DocumentFilenameException):
"""Exception for a document is added twice."""
def __init__(self) -> None:
super().__init__("A document was added twice")
class InputFileNotFoundException(DocumentFilenameException):
"""Exception for when an input file does not exist."""
def __init__(self) -> None:
super().__init__("Input file not found: make sure you typed it correctly.")
class InputFileNotReadableException(DocumentFilenameException):
"""Exception for when an input file exists but is not readable."""
def __init__(self) -> None:
super().__init__("You don't have permission to open the input file.")
class NonPDFOutputFileException(DocumentFilenameException):
"""Exception for when the output file is not a PDF."""
def __init__(self) -> None:
super().__init__("Safe PDF filename must end in '.pdf'")
class IllegalOutputFilenameException(DocumentFilenameException):
"""Exception for when the output file contains illegal characters."""
def __init__(self, char: str) -> None:
super().__init__(f"Illegal character: {char}")
class UnwriteableOutputDirException(DocumentFilenameException):
"""Exception for when the output file is not writeable."""
def __init__(self) -> None:
super().__init__("Safe PDF filename is not writable")
class NotSetInputFilenameException(DocumentFilenameException):
"""Exception for when the output filename is set before having an
associated input file."""
def __init__(self) -> None:
super().__init__("Input filename has not been set yet.")
class NotSetOutputFilenameException(DocumentFilenameException):
"""Exception for when the output filename is read before it has been set."""
def __init__(self) -> None:
super().__init__("Output filename has not been set yet.")
class NonExistantOutputDirException(DocumentFilenameException):
"""Exception for when the output dir does not exist."""
def __init__(self) -> None:
super().__init__("Output directory does not exist")
class OutputDirIsNotDirException(DocumentFilenameException):
"""Exception for when the specified output dir is not actually a dir."""
def __init__(self) -> None:
super().__init__("Specified output directory is actually not a directory")
class UnwriteableArchiveDirException(DocumentFilenameException):
"""Exception for when the archive directory cannot be created."""
def __init__(self) -> None:
super().__init__(
"Archive directory for storing unsafe documents cannot be created."
)
class SuffixNotApplicableException(DocumentFilenameException):
"""Exception for when the suffix cannot be applied to the output filename."""
def __init__(self) -> None:
super().__init__("Cannot set a suffix after setting an output filename")
def handle_document_errors(func: F) -> F:
"""Decorator to log document-related errors and exit gracefully."""
@functools.wraps(func)
def wrapper(*args, **kwargs): # type: ignore
try:
return func(*args, **kwargs)
except DocumentFilenameException as e:
if getattr(sys, "dangerzone_dev", False):
# Show the full traceback only on dev environments.
msg = "An exception occured while validating a document"
log.exception(msg)
click.echo(str(e))
sys.exit(1)
return cast(F, wrapper)
#### Container-related errors
class ContainerException(Exception):
pass
class ImageNotPresentException(ContainerException):
pass
class MultipleImagesFoundException(ContainerException):
pass
class ImageInstallationException(ContainerException):
pass
class NoContainerTechException(ContainerException):
def __init__(self, container_tech: str) -> None:
super().__init__(f"{container_tech} is not installed")
class NotAvailableContainerTechException(ContainerException):
def __init__(self, container_tech: str, error: str) -> None:
self.error = error
self.container_tech = container_tech
super().__init__(f"{container_tech} is not available")
class UnsupportedContainerRuntime(ContainerException):
pass
class ContainerPullException(ContainerException):
pass