airbnb/caravel

View on GitHub
RELEASING/verify_release.py

Summary

Maintainability
A
1 hr
Test Coverage
#!/usr/bin/python3
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import re
import subprocess
import sys
from typing import Optional

import requests

# Part 1: Verify SHA512 hash - this is the same as running `shasum -a 512 {release}` and comparing it against `{release}.sha512`


def get_sha512_hash(filename: str) -> str:
    """Run the shasum command on the file and return the SHA512 hash."""
    result = subprocess.run(["shasum", "-a", "512", filename], stdout=subprocess.PIPE)
    sha512_hash = result.stdout.decode().split()[0]
    return sha512_hash


def read_sha512_file(filename: str) -> str:
    """Read the corresponding .sha512 file and process its contents."""
    sha_filename = filename + ".sha512"
    with open(sha_filename) as file:
        lines = file.readlines()
        processed_sha = "".join(lines[1:]).replace(" ", "").replace("\n", "").lower()
    return processed_sha


def verify_sha512(filename: str) -> str:
    """Verify if the SHA512 hash of the file matches with the hash in the .sha512 file."""
    sha512_hash = get_sha512_hash(filename)
    sha512_file_content = read_sha512_file(filename)

    if sha512_hash == sha512_file_content:
        return "SHA verified"
    else:
        return "SHA failed"


# Part 2: Verify RSA key - this is the same as running `gpg --verify {release}.asc {release}` and comparing the RSA key and email address against the KEYS file


def get_gpg_info(filename: str) -> tuple[Optional[str], Optional[str]]:
    """Run the GPG verify command and extract RSA key and email address."""
    asc_filename = filename + ".asc"
    result = subprocess.run(
        ["gpg", "--verify", asc_filename, filename], capture_output=True
    )
    output = result.stderr.decode()

    rsa_key = re.search(r"RSA key ([0-9A-F]+)", output)
    email = re.search(r'issuer "([^"]+)"', output)

    rsa_key_result = rsa_key.group(1) if rsa_key else None
    email_result = email.group(1) if email else None

    # Debugging: print warnings if rsa_key or email is not found
    if rsa_key_result is None:
        print("Warning: No RSA key found in GPG verification output.")
    if email_result is None:
        print("Warning: No email address found in GPG verification output.")

    return rsa_key_result, email_result


def verify_rsa_key(rsa_key: str, email: Optional[str]) -> str:
    """Fetch the KEYS file and verify if the RSA key and email match."""
    url = "https://downloads.apache.org/superset/KEYS"
    response = requests.get(url)
    if response.status_code == 200:
        if rsa_key not in response.text:
            return "RSA key not found on KEYS page"

        # Check if email is None or not in response.text
        if email and email in response.text:
            return "RSA key and email verified against Apache KEYS file"
        elif email:
            return "RSA key verified, but Email not found on KEYS page"
        else:
            return "RSA key verified, but Email not available for verification"
    else:
        return "Failed to fetch KEYS file"


def verify_sha512_and_rsa(filename: str) -> None:
    """Verify SHA512 hash and RSA key."""
    sha_result = verify_sha512(filename)
    print(sha_result)

    rsa_key, email = get_gpg_info(filename)
    if rsa_key:
        rsa_result = verify_rsa_key(rsa_key, email)
        print(rsa_result)
    else:
        print("GPG verification failed: RSA key or email not found")


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python script.py <filename>")
    else:
        filename = sys.argv[1]
        verify_sha512_and_rsa(filename)