commit:     f1634540fe60edd3cbcff258b04d3b4e3aa31577
Author:     John Helmert III <ajak <AT> gentoo <DOT> org>
AuthorDate: Mon Jul 19 02:49:49 2021 +0000
Commit:     John Helmert III <ajak <AT> gentoo <DOT> org>
CommitDate: Mon Jul 19 02:54:53 2021 +0000
URL:        https://gitweb.gentoo.org/proj/security.git/commit/?id=f1634540

glsatool: add partial releasing functionality

This separates the previous functionality into a `glsatool new` and adds
some new functionality as `glsatool release`. Currently only fetches the
GLSA XML and text, places the XML in your glsa.git repository, and adds
the necessary headers to the mail text.

Signed-off-by: John Helmert III <ajak <AT> gentoo.org>

 bin/GLSATool.py | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 90 insertions(+), 9 deletions(-)

diff --git a/bin/GLSATool.py b/bin/GLSATool.py
index c7fc804..a2a1b1b 100644
--- a/bin/GLSATool.py
+++ b/bin/GLSATool.py
@@ -2,6 +2,7 @@ from configparser import ConfigParser
 import argparse
 import os
 import re
+import sys
 
 import bugzilla
 import requests
@@ -19,9 +20,15 @@ class GLSATool:
         self.auth = glsamaker_key
         self.bgo = bugzilla.Bugzilla('https://bugs.gentoo.org',
                                      api_key=bgo_key, force_rest=True)
-
-    def get_csrf_token(self):
-        soup = bs(self.request('/glsas/new'), features='lxml')
+        config_path = os.path.join(os.path.expanduser('~'),
+                                   '.config', 'glsatool')
+        c = ConfigParser()
+        c.read(config_path)
+        self.glsa_path = c['default']['glsa']
+        self.from_str = c['default']['from']
+
+    def get_csrf_token(self, path):
+        soup = bs(self.request(path), features='lxml')
         csrf_token = \
             soup.find('input', {'name': 'authenticity_token'})['value']
         return csrf_token
@@ -45,6 +52,64 @@ class GLSATool:
             raise RuntimeError(path + ': ' + str(response.status_code))
         return response.text
 
+    def get_int_input(self, msg):
+        while True:
+            i = input(msg)
+            try:
+                return int(i)
+            except (ValueError, EOFError):
+                continue
+
+    def release_glsa(self, num=None):
+        if not num:
+            soup = bs(self.request('/glsas/drafts'), features='lxml')
+            glsas = soup.find_all('tr', {'class': True})
+            for idx, item in enumerate(glsas):
+                print('[{0}] {1}'.format(idx, item.find('a').text))
+            i = self.get_int_input("Which GLSA to release? ")
+            print("Selected '{0}'".format(glsas[i].a.text))
+            num = glsas[i].a['href'][-4:]
+
+        prepare_path = '/glsas/{}/prepare_release'.format(num)
+        release_path = '/glsas/{}/release'.format(num)
+        xml_path = '/glsas/{}/download.xml'.format(num)
+        txt_path = '/glsas/{}/download.txt'.format(num)
+
+        data = {
+            'value': 'Release &gt;',
+            'authenticity_token': self.get_csrf_token(prepare_path)
+        }
+
+        # Click the "release" button
+        released_soup = bs(self.request(release_path, method='POST', 
data=data),
+                           features='lxml')
+        glsa_id = 'glsa-' + released_soup.find('strong').text.split()[1]
+
+        # Grab the xml
+        xml = self.request(xml_path)
+        xml_filename = '{}.xml'.format(glsa_id)
+        xml_path = os.path.join(self.glsa_path, xml_filename)
+
+        # Write and (TODO) commit it
+        with open(xml_path, 'w') as f:
+            f.write(xml)
+        print("Wrote {}".format(xml_filename))
+
+        # Grab the mail text
+        txt = self.request(txt_path)
+        txt_filename = '{}.txt'.format(glsa_id)
+
+        # Write it
+        with open(txt_filename, 'w') as f:
+            f.write('From: {}\n'.format(self.from_str))
+            f.write('Reply-To: [email protected]\n')
+            f.write(txt)
+        print("Wrote {}".format(txt_filename))
+
+        # TODO:
+        # Mail it
+        # Close bugs
+
     def new_whiteboard(self, old_whiteboard):
         regex = re.compile('[A-C~][0-4] \[.*\]')
         severity = old_whiteboard[:2]
@@ -78,7 +143,7 @@ class GLSATool:
             'access': 'public',
             'import_references': '1',
             'what': 'request',  # ???
-            'authenticity_token': self.get_csrf_token()
+            'authenticity_token': self.get_csrf_token('/glsas/new')
         }
         self.request('/glsas', method='POST', data=data)
         print("GLSA request filed")
@@ -104,10 +169,26 @@ def bgo_key():
 
 def glsatool():
     parser = argparse.ArgumentParser()
-    parser.add_argument('-b', '--bugs', required=True, nargs='+')
-    parser.add_argument('-t', '--title', required=True)
+    subparsers = parser.add_subparsers(dest='command')
+
+    new_parser = subparsers.add_parser('new')
+    new_parser.add_argument('-b', '--bugs', required=True, nargs='+')
+    new_parser.add_argument('-t', '--title', required=True)
+
+    release_parser = subparsers.add_parser('release')
+    release_parser.add_argument('-i', '--id')
+
     args = parser.parse_args()
     auth = glsamaker_key()
-    for bug in args.bugs:
-        CVETool(auth, 'dobug', [bug])
-    GLSATool(auth, bgo_key()).new_glsa(args.title, args.bugs)
+    gtool = GLSATool(auth, bgo_key())
+    if args.command == 'new':
+        for bug in args.bugs:
+            CVETool(auth, 'dobug', [bug])
+        gtool.new_glsa(args.title, args.bugs)
+    elif args.command == 'release':
+        if args.id:
+            gtool.release_glsa(num=args.id)
+        else:
+            gtool.release_glsa()
+    else:
+        print("No command given!")

Reply via email to