Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collection of suppressed Ruff Linter errors to review (noqa) #838

Open
L77H opened this issue Jun 28, 2024 · 1 comment
Open

Collection of suppressed Ruff Linter errors to review (noqa) #838

L77H opened this issue Jun 28, 2024 · 1 comment

Comments

@L77H
Copy link
Contributor

L77H commented Jun 28, 2024

The following are currently suppressed linter errors, these should be reviewed and decided on if they can be resolved or kept suppressed with #noqa.
This issue will be updated with more errors if when they come up.

PLW2901: redefined-loop-name
https://github.com/secure-systems-lab/securesystemslib/blob/db0a9a5afe9604f4e614e1979d0559832df64a08/securesystemslib/dsse.py#L63C1-L73C54

S603: subprocess-without-shell-equals-true

gpg_version_cmd = shlex.split(f"{gnupg} --version")
try:
subprocess.run(
gpg_version_cmd, # noqa: S603
capture_output=True,
timeout=timeout,
check=True,
)
return True

N818: error-suffix-on-exception-name
move this to exceptions.py?

class UnsupportedKeyType(Exception): # noqa: N818
pass

PLR2004: magic-value-comparison
https://github.com/secure-systems-lab/securesystemslib/blob/db0a9a5afe9604f4e614e1979d0559832df64a08/securesystemslib/_gpg/util.py
https://github.com/secure-systems-lab/securesystemslib/blob/db0a9a5afe9604f4e614e1979d0559832df64a08/tests/test_gpg.py

PLW0603: global-statement

global _PYKCS11LIB # noqa: PLW0603

PLR0912: too-many-branches

# ruff: noqa: PLR0912, PLR0915
def parse_signature_packet(
data,
supported_signature_types=None,
supported_hash_algorithms=None,
include_info=False,
):
"""
<Purpose>
Parse the signature information on an RFC4880-encoded binary signature data
buffer.
NOTE: Older gpg versions (< FULLY_SUPPORTED_MIN_VERSION) might only
reveal the partial key id. It is the callers responsibility to determine
the full keyid based on the partial keyid, e.g. by exporting the related
public and replacing the partial keyid with the full keyid.
<Arguments>
data:
the RFC4880-encoded binary signature data buffer as described in
section 5.2 (and 5.2.3.1).
supported_signature_types: (optional)
a set of supported signature_types, the signature packet may be
(see securesystemslib._gpg.constants for available types). If None is
specified the signature packet must be of type SIGNATURE_TYPE_BINARY.
supported_hash_algorithms: (optional)
a set of supported hash algorithm ids, the signature packet
may use. Available ids are SHA1, SHA256, SHA512 (see
securesystemslib._gpg.constants). If None is specified, the signature
packet must use SHA256.
include_info: (optional)
a boolean that indicates whether an opaque dictionary should be
added to the returned signature under the key "info". Default is
False.
<Exceptions>
ValueError: if the signature packet is not supported or the data is
malformed
IndexError: if the signature packet is incomplete
<Side Effects>
None.
<Returns>
A signature dict with the following special characteristics:
- The "keyid" field is an empty string if it cannot be determined
- The "short_keyid" is not added if it cannot be determined
- At least one of non-empty "keyid" or "short_keyid" are part of the
signature
"""
if not supported_signature_types:
supported_signature_types = {SIGNATURE_TYPE_BINARY}
if not supported_hash_algorithms:
supported_hash_algorithms = {SHA256}
_, header_len, _, packet_len = gpg_util.parse_packet_header(
data, PACKET_TYPE_SIGNATURE
)
data = bytearray(data[header_len:packet_len])
ptr = 0
# we get the version number, which we also expect to be v4, or we bail
# FIXME: support v3 type signatures (which I haven't seen in the wild)
version_number = data[ptr]
ptr += 1
if version_number not in SUPPORTED_SIGNATURE_PACKET_VERSIONS:
raise ValueError(
"Signature version '{}' not supported, must be one of "
"{}.".format(version_number, SUPPORTED_SIGNATURE_PACKET_VERSIONS)
)
# Per default we only parse "signatures of a binary document". Other types
# may be allowed by passing type constants via `supported_signature_types`.
# Types include revocation signatures, key binding signatures, persona
# certifications, etc. (see RFC 4880 section 5.2.1.).
signature_type = data[ptr]
ptr += 1
if signature_type not in supported_signature_types:
raise ValueError(
"Signature type '{}' not supported, must be one of {} "
"(see RFC4880 5.2.1. Signature Types).".format(
signature_type, supported_signature_types
)
)
signature_algorithm = data[ptr]
ptr += 1
if signature_algorithm not in SUPPORTED_SIGNATURE_ALGORITHMS:
raise ValueError(
"Signature algorithm '{}' not "
"supported, please verify that your gpg configuration is creating "
"either DSA, RSA, or EdDSA signatures (see RFC4880 9.1. Public-Key "
"Algorithms).".format(signature_algorithm)
)
key_type = SUPPORTED_SIGNATURE_ALGORITHMS[signature_algorithm]["type"]
handler = SIGNATURE_HANDLERS[key_type]
hash_algorithm = data[ptr]
ptr += 1
if hash_algorithm not in supported_hash_algorithms:
raise ValueError(
"Hash algorithm '{}' not supported, must be one of {}"
" (see RFC4880 9.4. Hash Algorithms).".format(
hash_algorithm, supported_hash_algorithms
)
)
# Obtain the hashed octets
hashed_octet_count = struct.unpack(">H", data[ptr : ptr + 2])[0]
ptr += 2
hashed_subpackets = data[ptr : ptr + hashed_octet_count]
hashed_subpacket_info = gpg_util.parse_subpackets(hashed_subpackets)
# Check whether we were actually able to read this much hashed octets
if len(hashed_subpackets) != hashed_octet_count: # pragma: no cover
raise ValueError(
"This signature packet seems to be corrupted."
"It is missing hashed octets!"
)
ptr += hashed_octet_count
other_headers_ptr = ptr
unhashed_octet_count = struct.unpack(">H", data[ptr : ptr + 2])[0]
ptr += 2
unhashed_subpackets = data[ptr : ptr + unhashed_octet_count]
unhashed_subpacket_info = gpg_util.parse_subpackets(unhashed_subpackets)
ptr += unhashed_octet_count
# Use the info dict to return further signature information that may be
# needed for intermediate processing, but does not have to be on the eventual
# signature datastructure
info = {
"signature_type": signature_type,
"hash_algorithm": hash_algorithm,
"creation_time": None,
"subpackets": {},
}
keyid = ""
short_keyid = ""
# Parse "Issuer" (short keyid) and "Issuer Fingerprint" (full keyid) type
# subpackets
# Strategy: Loop over all unhashed and hashed subpackets (in that order!) and
# store only the last of a type. Due to the order in the loop, hashed
# subpackets are prioritized over unhashed subpackets (see NOTEs below).
# NOTE: A subpacket may be found either in the hashed or unhashed subpacket
# sections of a signature. If a subpacket is not hashed, then the information
# in it cannot be considered definitive because it is not part of the
# signature proper. (see RFC4880 5.2.3.2.)
# NOTE: Signatures may contain conflicting information in subpackets. In most
# cases, an implementation SHOULD use the last subpacket, but MAY use any
# conflict resolution scheme that makes more sense. (see RFC4880 5.2.4.1.)
for idx, subpacket_tuple in enumerate(
unhashed_subpacket_info + hashed_subpacket_info
):
# The idx indicates if the info is from the unhashed (first) or
# hashed (second) of the above concatenated lists
is_hashed = idx >= len(unhashed_subpacket_info)
subpacket_type, subpacket_data = subpacket_tuple
# Warn if expiration subpacket is not hashed
if subpacket_type == KEY_EXPIRATION_SUBPACKET:
if not is_hashed:
log.warning(
"Expiration subpacket not hashed, gpg client possibly "
"exporting a weakly configured key."
)
# Full keyids are only available in newer signatures
# (see RFC4880 and rfc4880bis-06 5.2.3.1.)
if subpacket_type == FULL_KEYID_SUBPACKET: # pragma: no cover
# Exclude from coverage for consistent results across test envs
# NOTE: The first byte of the subpacket payload is a version number
# (see rfc4880bis-06 5.2.3.28.)
keyid = binascii.hexlify(subpacket_data[1:]).decode("ascii")
# We also return the short keyid, because the full might not be available
if subpacket_type == PARTIAL_KEYID_SUBPACKET:
short_keyid = binascii.hexlify(subpacket_data).decode("ascii")
if subpacket_type == SIG_CREATION_SUBPACKET:
info["creation_time"] = struct.unpack(">I", subpacket_data)[0]
info["subpackets"][subpacket_type] = subpacket_data
# Fail if there is no keyid at all (this should not happen)
if not (keyid or short_keyid): # pragma: no cover
raise ValueError(
"This signature packet seems to be corrupted. It does "
"not have an 'Issuer' or 'Issuer Fingerprint' subpacket (see RFC4880 "
"and rfc4880bis-06 5.2.3.1. Signature Subpacket Specification)."
)
# Fail if keyid and short keyid are specified but don't match
if keyid and not keyid.endswith(short_keyid): # pragma: no cover
raise ValueError(
"This signature packet seems to be corrupted. The key ID "
"'{}' of the 'Issuer' subpacket must match the lower 64 bits of the "
"fingerprint '{}' of the 'Issuer Fingerprint' subpacket (see RFC4880 "
"and rfc4880bis-06 5.2.3.28. Issuer Fingerprint).".format(
short_keyid, keyid
)
)
if not info["creation_time"]: # pragma: no cover
raise ValueError(
"This signature packet seems to be corrupted. It does "
"not have a 'Signature Creation Time' subpacket (see RFC4880 5.2.3.4 "
"Signature Creation Time)."
)
# Uncomment this variable to obtain the left-hash-bits information (used for
# early rejection)
# left_hash_bits = struct.unpack(">H", data[ptr:ptr+2])[0]
ptr += 2
# Finally, fetch the actual signature (as opposed to signature metadata).
signature = handler.get_signature_params(data[ptr:])
signature_data = {
"keyid": "{}".format(keyid),
"other_headers": binascii.hexlify(data[:other_headers_ptr]).decode(
"ascii"
),
"signature": binascii.hexlify(signature).decode("ascii"),
}
if short_keyid: # pragma: no branch
signature_data["short_keyid"] = short_keyid
if include_info:
signature_data["info"] = info
return signature_data

PLR0915: too-many-statements

# ruff: noqa: PLR0912, PLR0915
def parse_signature_packet(
data,
supported_signature_types=None,
supported_hash_algorithms=None,
include_info=False,
):
"""
<Purpose>
Parse the signature information on an RFC4880-encoded binary signature data
buffer.
NOTE: Older gpg versions (< FULLY_SUPPORTED_MIN_VERSION) might only
reveal the partial key id. It is the callers responsibility to determine
the full keyid based on the partial keyid, e.g. by exporting the related
public and replacing the partial keyid with the full keyid.
<Arguments>
data:
the RFC4880-encoded binary signature data buffer as described in
section 5.2 (and 5.2.3.1).
supported_signature_types: (optional)
a set of supported signature_types, the signature packet may be
(see securesystemslib._gpg.constants for available types). If None is
specified the signature packet must be of type SIGNATURE_TYPE_BINARY.
supported_hash_algorithms: (optional)
a set of supported hash algorithm ids, the signature packet
may use. Available ids are SHA1, SHA256, SHA512 (see
securesystemslib._gpg.constants). If None is specified, the signature
packet must use SHA256.
include_info: (optional)
a boolean that indicates whether an opaque dictionary should be
added to the returned signature under the key "info". Default is
False.
<Exceptions>
ValueError: if the signature packet is not supported or the data is
malformed
IndexError: if the signature packet is incomplete
<Side Effects>
None.
<Returns>
A signature dict with the following special characteristics:
- The "keyid" field is an empty string if it cannot be determined
- The "short_keyid" is not added if it cannot be determined
- At least one of non-empty "keyid" or "short_keyid" are part of the
signature
"""
if not supported_signature_types:
supported_signature_types = {SIGNATURE_TYPE_BINARY}
if not supported_hash_algorithms:
supported_hash_algorithms = {SHA256}
_, header_len, _, packet_len = gpg_util.parse_packet_header(
data, PACKET_TYPE_SIGNATURE
)
data = bytearray(data[header_len:packet_len])
ptr = 0
# we get the version number, which we also expect to be v4, or we bail
# FIXME: support v3 type signatures (which I haven't seen in the wild)
version_number = data[ptr]
ptr += 1
if version_number not in SUPPORTED_SIGNATURE_PACKET_VERSIONS:
raise ValueError(
"Signature version '{}' not supported, must be one of "
"{}.".format(version_number, SUPPORTED_SIGNATURE_PACKET_VERSIONS)
)
# Per default we only parse "signatures of a binary document". Other types
# may be allowed by passing type constants via `supported_signature_types`.
# Types include revocation signatures, key binding signatures, persona
# certifications, etc. (see RFC 4880 section 5.2.1.).
signature_type = data[ptr]
ptr += 1
if signature_type not in supported_signature_types:
raise ValueError(
"Signature type '{}' not supported, must be one of {} "
"(see RFC4880 5.2.1. Signature Types).".format(
signature_type, supported_signature_types
)
)
signature_algorithm = data[ptr]
ptr += 1
if signature_algorithm not in SUPPORTED_SIGNATURE_ALGORITHMS:
raise ValueError(
"Signature algorithm '{}' not "
"supported, please verify that your gpg configuration is creating "
"either DSA, RSA, or EdDSA signatures (see RFC4880 9.1. Public-Key "
"Algorithms).".format(signature_algorithm)
)
key_type = SUPPORTED_SIGNATURE_ALGORITHMS[signature_algorithm]["type"]
handler = SIGNATURE_HANDLERS[key_type]
hash_algorithm = data[ptr]
ptr += 1
if hash_algorithm not in supported_hash_algorithms:
raise ValueError(
"Hash algorithm '{}' not supported, must be one of {}"
" (see RFC4880 9.4. Hash Algorithms).".format(
hash_algorithm, supported_hash_algorithms
)
)
# Obtain the hashed octets
hashed_octet_count = struct.unpack(">H", data[ptr : ptr + 2])[0]
ptr += 2
hashed_subpackets = data[ptr : ptr + hashed_octet_count]
hashed_subpacket_info = gpg_util.parse_subpackets(hashed_subpackets)
# Check whether we were actually able to read this much hashed octets
if len(hashed_subpackets) != hashed_octet_count: # pragma: no cover
raise ValueError(
"This signature packet seems to be corrupted."
"It is missing hashed octets!"
)
ptr += hashed_octet_count
other_headers_ptr = ptr
unhashed_octet_count = struct.unpack(">H", data[ptr : ptr + 2])[0]
ptr += 2
unhashed_subpackets = data[ptr : ptr + unhashed_octet_count]
unhashed_subpacket_info = gpg_util.parse_subpackets(unhashed_subpackets)
ptr += unhashed_octet_count
# Use the info dict to return further signature information that may be
# needed for intermediate processing, but does not have to be on the eventual
# signature datastructure
info = {
"signature_type": signature_type,
"hash_algorithm": hash_algorithm,
"creation_time": None,
"subpackets": {},
}
keyid = ""
short_keyid = ""
# Parse "Issuer" (short keyid) and "Issuer Fingerprint" (full keyid) type
# subpackets
# Strategy: Loop over all unhashed and hashed subpackets (in that order!) and
# store only the last of a type. Due to the order in the loop, hashed
# subpackets are prioritized over unhashed subpackets (see NOTEs below).
# NOTE: A subpacket may be found either in the hashed or unhashed subpacket
# sections of a signature. If a subpacket is not hashed, then the information
# in it cannot be considered definitive because it is not part of the
# signature proper. (see RFC4880 5.2.3.2.)
# NOTE: Signatures may contain conflicting information in subpackets. In most
# cases, an implementation SHOULD use the last subpacket, but MAY use any
# conflict resolution scheme that makes more sense. (see RFC4880 5.2.4.1.)
for idx, subpacket_tuple in enumerate(
unhashed_subpacket_info + hashed_subpacket_info
):
# The idx indicates if the info is from the unhashed (first) or
# hashed (second) of the above concatenated lists
is_hashed = idx >= len(unhashed_subpacket_info)
subpacket_type, subpacket_data = subpacket_tuple
# Warn if expiration subpacket is not hashed
if subpacket_type == KEY_EXPIRATION_SUBPACKET:
if not is_hashed:
log.warning(
"Expiration subpacket not hashed, gpg client possibly "
"exporting a weakly configured key."
)
# Full keyids are only available in newer signatures
# (see RFC4880 and rfc4880bis-06 5.2.3.1.)
if subpacket_type == FULL_KEYID_SUBPACKET: # pragma: no cover
# Exclude from coverage for consistent results across test envs
# NOTE: The first byte of the subpacket payload is a version number
# (see rfc4880bis-06 5.2.3.28.)
keyid = binascii.hexlify(subpacket_data[1:]).decode("ascii")
# We also return the short keyid, because the full might not be available
if subpacket_type == PARTIAL_KEYID_SUBPACKET:
short_keyid = binascii.hexlify(subpacket_data).decode("ascii")
if subpacket_type == SIG_CREATION_SUBPACKET:
info["creation_time"] = struct.unpack(">I", subpacket_data)[0]
info["subpackets"][subpacket_type] = subpacket_data
# Fail if there is no keyid at all (this should not happen)
if not (keyid or short_keyid): # pragma: no cover
raise ValueError(
"This signature packet seems to be corrupted. It does "
"not have an 'Issuer' or 'Issuer Fingerprint' subpacket (see RFC4880 "
"and rfc4880bis-06 5.2.3.1. Signature Subpacket Specification)."
)
# Fail if keyid and short keyid are specified but don't match
if keyid and not keyid.endswith(short_keyid): # pragma: no cover
raise ValueError(
"This signature packet seems to be corrupted. The key ID "
"'{}' of the 'Issuer' subpacket must match the lower 64 bits of the "
"fingerprint '{}' of the 'Issuer Fingerprint' subpacket (see RFC4880 "
"and rfc4880bis-06 5.2.3.28. Issuer Fingerprint).".format(
short_keyid, keyid
)
)
if not info["creation_time"]: # pragma: no cover
raise ValueError(
"This signature packet seems to be corrupted. It does "
"not have a 'Signature Creation Time' subpacket (see RFC4880 5.2.3.4 "
"Signature Creation Time)."
)
# Uncomment this variable to obtain the left-hash-bits information (used for
# early rejection)
# left_hash_bits = struct.unpack(">H", data[ptr:ptr+2])[0]
ptr += 2
# Finally, fetch the actual signature (as opposed to signature metadata).
signature = handler.get_signature_params(data[ptr:])
signature_data = {
"keyid": "{}".format(keyid),
"other_headers": binascii.hexlify(data[:other_headers_ptr]).decode(
"ascii"
),
"signature": binascii.hexlify(signature).decode("ascii"),
}
if short_keyid: # pragma: no branch
signature_data["short_keyid"] = short_keyid
if include_info:
signature_data["info"] = info
return signature_data

PLR0913: too-many-arguments

def __init__( # noqa: PLR0913
self,
keyid: str,
keytype: str,
scheme: str,
keyval: Dict[str, Any],
unrecognized_fields: Optional[Dict[str, Any]] = None,
):

E402: module-import-not-at-top-of-file
https://github.com/secure-systems-lab/securesystemslib/blob/db0a9a5afe9604f4e614e1979d0559832df64a08/securesystemslib/_gpg/dsa.py#L30C15-L33

# ruff: noqa: E402
from securesystemslib import exceptions
from securesystemslib._gpg import util as gpg_util
from securesystemslib._gpg.exceptions import PacketParsingError

# ruff: noqa: E402
from securesystemslib import exceptions
from securesystemslib._gpg import constants
from securesystemslib._gpg.exceptions import PacketParsingError

@lukpueh
Copy link
Member

lukpueh commented Jul 2, 2024

I'm fine if we just suppress above linter warnings in the _gpg subpackage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants