You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2023/12/18 18:47:04 UTC

(superset) branch master updated: feat(releasing): adding SHA512 and RSA signature validation script to verify releases (#26278)

This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 84ac72f550 feat(releasing): adding SHA512 and RSA signature validation script to verify releases (#26278)
84ac72f550 is described below

commit 84ac72f55030b56405af1165acba442f5ace38d6
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Mon Dec 18 11:46:56 2023 -0700

    feat(releasing): adding SHA512 and RSA signature validation script to verify releases (#26278)
---
 RELEASING/README.md                |  15 ++++++
 RELEASING/validate_this_release.sh |  54 ++++++++++++++++++++
 RELEASING/verify_release.py        | 101 +++++++++++++++++++++++++++++++++++++
 superset-frontend/package.json     |   3 +-
 4 files changed, 172 insertions(+), 1 deletion(-)

diff --git a/RELEASING/README.md b/RELEASING/README.md
index b007a89170..b0cad547ca 100644
--- a/RELEASING/README.md
+++ b/RELEASING/README.md
@@ -388,8 +388,23 @@ The script will generate the email text that should be sent to dev@superset.apac
 
 ## Validating a release
 
+Official instructions:
 https://www.apache.org/info/verification.html
 
+We now have a handy script for anyone validating a release to use. The core of it is in this very folder, `verify_release.py`. Just make sure you have all three release files in the same directory (`{some version}.tar.gz`, `{some version}.tar.gz.asc` and `{some version}tar.gz.sha512`). Then you can pass this script the path to the `.gz` file like so:
+`python verify_release.py ~/path/tp/apache-superset-{version/candidate}-source.tar.gz`
+
+
+If all goes well, you will see this result in your terminal:
+```bash
+SHA-512 verified
+RSA key verified
+```
+
+There are also additional support scripts leveraging this to make it easy for those downloading a release to test it in-situ. You can do either of the following to validate these release assets:
+* `cd` into `superset-frontend` and run `npm run validate-release`
+* `cd` into `RELEASES` and run `./validate_this_release.sh`
+
 ## Publishing a successful release
 
 Upon a successful vote, you'll have to copy the folder into the non-"dev/" folder.
diff --git a/RELEASING/validate_this_release.sh b/RELEASING/validate_this_release.sh
new file mode 100755
index 0000000000..6d2b6fca5f
--- /dev/null
+++ b/RELEASING/validate_this_release.sh
@@ -0,0 +1,54 @@
+# 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.
+
+#!/bin/bash
+
+# Function to determine Python command
+get_python_command() {
+    if command -v python3 &>/dev/null; then
+        echo "python3"
+    else
+        echo "python"
+    fi
+}
+
+# Function to determine Pip command
+get_pip_command() {
+    if command -v pip3 &>/dev/null; then
+        echo "pip3"
+    else
+        echo "pip"
+    fi
+}
+
+PYTHON=$(get_python_command)
+PIP=$(get_pip_command)
+
+# Get the release directory's path. If you unzip an Apache release and just run the npm script to validate the release, this will be a file name like `apache-superset-x.x.xrcx-source.tar.gz`
+RELEASE_DIR_NAME="../../$(basename "$(dirname "$(pwd)")").tar.gz"
+
+# Install dependencies from requirements.txt if the file exists
+if [ -f "path/to/requirements.txt" ]; then
+    echo "Installing Python dependencies..."
+    $PYTHON -m $PIP install -r path/to/requirements.txt
+fi
+
+# echo $PYTHON
+# echo $RELEASE_DIR_NAME
+
+# Run the Python script with the parent directory name as an argument
+$PYTHON ../RELEASING/verify_release.py "$RELEASE_DIR_NAME"
diff --git a/RELEASING/verify_release.py b/RELEASING/verify_release.py
new file mode 100755
index 0000000000..067154e190
--- /dev/null
+++ b/RELEASING/verify_release.py
@@ -0,0 +1,101 @@
+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)
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index d8ed6f62e8..f12989cd38 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -70,7 +70,8 @@
     "storybook": "cross-env NODE_ENV=development BABEL_ENV=development start-storybook -p 6006",
     "tdd": "cross-env NODE_ENV=test jest --watch",
     "test": "cross-env NODE_ENV=test jest",
-    "type": "tsc --noEmit"
+    "type": "tsc --noEmit",
+    "validate-release": "../RELEASING/validate_this_release.sh"
   },
   "browserslist": [
     "last 3 chrome versions",