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

Provide support script for adding new test subject #817

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Tests/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
htmlcov/
.coverage
.secret
87 changes: 87 additions & 0 deletions Tests/add_subject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
# vim: set ts=4 sw=4 et:
#
# add_subject.py
#
# (c) Matthias Büchse <[email protected]>
# SPDX-License-Identifier: Apache-2.0
import base64
import getpass
import os
import os.path
import re
import shutil
import subprocess
import sys

try:
from passlib.context import CryptContext
import argon2 # noqa:F401
except ImportError:
print('Missing passlib and/or argon2. Please do:\npip install passlib argon2_cffi', file=sys.stderr)
sys.exit(1)

# see ../compliance-monitor/monitor.py
CRYPTCTX = CryptContext(schemes=('argon2', 'bcrypt'), deprecated='auto')
SSH_KEYGEN = shutil.which('ssh-keygen')
SUBJECT_RE = re.compile(r"[a-zA-Z0-9_\-]+")


def main(argv, cwd):
if len(argv) != 1:
raise RuntimeError("Need to supply precisely one argument: name of subject")
subject = argv[0]
print(f"Attempt to add subject {subject!r}")
keyfile_path = os.path.join(cwd, '.secret', 'keyfile')
tokenfile_path = os.path.join(cwd, '.secret', 'tokenfile')
if os.path.exists(keyfile_path):
raise RuntimeError(f"Keyfile {keyfile_path} already present. Please proceed manually")
if os.path.exists(tokenfile_path):
raise RuntimeError(f"Tokenfile {tokenfile_path} already present. Please proceed manually")
if not SUBJECT_RE.fullmatch(subject):
raise RuntimeError(f"Subject name {subject!r} using disallowed characters")
sanitized_subject = subject.replace('-', '_')
print("Creating API key...")
while True:
password = getpass.getpass("Enter passphrase: ")
if password == getpass.getpass("Repeat passphrase: "):
break
print("No match. Try again...")
token = base64.b64encode(f"{subject}:{password}".encode('utf-8'))
hash_ = CRYPTCTX.hash(password)
with open(tokenfile_path, "wb") as fileobj:
fileobj.write(token)

Check failure

Code scanning / CodeQL

Clear-text storage of sensitive information High

This expression stores
sensitive data (password)
as clear text.

Copilot Autofix AI 8 days ago

To fix the problem, we need to ensure that the password is encrypted before being stored in the token file. We can use the cryptography library to encrypt the password before encoding it in base64 and writing it to the file. This will ensure that the password is not stored in clear text.

  1. Import the necessary modules from the cryptography library.
  2. Generate a key for encryption.
  3. Encrypt the password using the generated key.
  4. Encode the encrypted password in base64 and store it in the token file.
Suggested changeset 1
Tests/add_subject.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Tests/add_subject.py b/Tests/add_subject.py
--- a/Tests/add_subject.py
+++ b/Tests/add_subject.py
@@ -8,2 +8,3 @@
 import base64
+from cryptography.fernet import Fernet
 import getpass
@@ -50,3 +51,6 @@
         print("No match. Try again...")
-    token = base64.b64encode(f"{subject}:{password}".encode('utf-8'))
+    key = Fernet.generate_key()
+    cipher_suite = Fernet(key)
+    encrypted_password = cipher_suite.encrypt(password.encode('utf-8'))
+    token = base64.b64encode(f"{subject}:{encrypted_password.decode('utf-8')}".encode('utf-8'))
     hash_ = CRYPTCTX.hash(password)
@@ -54,2 +58,4 @@
         fileobj.write(token)
+    fileobj.write(b'\n')
+    fileobj.write(key)
     print("Creating key file using `ssh-keygen`...")
EOF
@@ -8,2 +8,3 @@
import base64
from cryptography.fernet import Fernet
import getpass
@@ -50,3 +51,6 @@
print("No match. Try again...")
token = base64.b64encode(f"{subject}:{password}".encode('utf-8'))
key = Fernet.generate_key()
cipher_suite = Fernet(key)
encrypted_password = cipher_suite.encrypt(password.encode('utf-8'))
token = base64.b64encode(f"{subject}:{encrypted_password.decode('utf-8')}".encode('utf-8'))
hash_ = CRYPTCTX.hash(password)
@@ -54,2 +58,4 @@
fileobj.write(token)
fileobj.write(b'\n')
fileobj.write(key)
print("Creating key file using `ssh-keygen`...")
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Erm what? And the key? Where am I storing the key???

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without spending an hour to understand what's required here, I can not comment on this, sorry.
If we can avoid storing sensitive data in plaintext, we should of course. I have no idea whether or not that is possible without major effort. If we need to store it, we should adjust the permissions / umask to 0600 (rw-------).

print("Creating key file using `ssh-keygen`...")
subprocess.check_call([SSH_KEYGEN, '-t', 'ed25519', '-C', sanitized_subject, '-f', keyfile_path, '-N', '', '-q'])
with open(keyfile_path + '.pub', "r") as fileobj:
pubkey_components = fileobj.readline().split()
print(f'''
The following SECRET files have been created:

- {keyfile_path}
- {tokenfile_path}

They are required for submitting test reports. You MUST keep them secure and safe.

Insert the following snippet into compliance-monitor/bootstrap.yaml:

- subject: {subject}
api_keys:
- "{hash_}"
keys:
- public_key: "{pubkey_components[1]}"
public_key_type: "{pubkey_components[0]}"
public_key_name: "primary"

Make sure to submit a pull request with the changed file. Otherwise, the reports cannot be submitted.
''', end='')


if __name__ == "__main__":
try:
sys.exit(main(sys.argv[1:], cwd=os.path.dirname(sys.argv[0]) or os.getcwd()) or 0)
except RuntimeError as e:
print(str(e), file=sys.stderr)
sys.exit(1)
except KeyboardInterrupt:
print("Interrupted", file=sys.stderr)
Copy link
Member

@garloff garloff Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add sys.exit(128+signal.SIGINT) here?
(Needs an import signal then of course.)
This would make the exit code the same that you get if you did not catch the exception ...