You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2015/07/30 22:57:04 UTC
svn commit: r1693501 - in /subversion/trunk/tools/dist/security: ./
__init__.py adviser.py parser.py
Author: brane
Date: Thu Jul 30 20:57:04 2015
New Revision: 1693501
URL: http://svn.apache.org/r1693501
Log:
Begin writing better scripting support for generating advisory mails
and text files for our web site.
* security/security: New directory.
* security/security/__init__.py: New; makes this directory a Python module.
* security/security/parser.py: New; parser for notifications and patches.
* security/security/adviser.py: New; text-based advisory file generator.
Added:
subversion/trunk/tools/dist/security/ (with props)
subversion/trunk/tools/dist/security/__init__.py (with props)
subversion/trunk/tools/dist/security/adviser.py (with props)
subversion/trunk/tools/dist/security/parser.py (with props)
Propchange: subversion/trunk/tools/dist/security/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Jul 30 20:57:04 2015
@@ -0,0 +1,2 @@
+__pycache__
+*.pyc
Added: subversion/trunk/tools/dist/security/__init__.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/dist/security/__init__.py?rev=1693501&view=auto
==============================================================================
--- subversion/trunk/tools/dist/security/__init__.py (added)
+++ subversion/trunk/tools/dist/security/__init__.py Thu Jul 30 20:57:04 2015
@@ -0,0 +1,18 @@
+#
+# 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.
+#
Propchange: subversion/trunk/tools/dist/security/__init__.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: subversion/trunk/tools/dist/security/adviser.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/dist/security/adviser.py?rev=1693501&view=auto
==============================================================================
--- subversion/trunk/tools/dist/security/adviser.py (added)
+++ subversion/trunk/tools/dist/security/adviser.py Thu Jul 30 20:57:04 2015
@@ -0,0 +1,62 @@
+#
+# 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.
+#
+
+"""
+Generator of textual advisories for subversion.a.o/security
+"""
+
+from __future__ import absolute_import
+
+import os
+import sys
+
+
+def __write_advisory(metadata, fd):
+ """
+ Create a textual representation of the advisory described
+ by METADATA and write it to the file descriptor FD.
+ """
+
+ fd.write(metadata.advisory.get_text())
+ if not metadata.patches:
+ return
+
+ fd.write('\nPatches:'
+ '\n========\n')
+ for patch in metadata.patches:
+ fd.write('\n Patch against ' + patch.base_version + ':\n'
+ '[[[\n')
+ fd.write(patch.get_text())
+ fd.write(']]]\n')
+
+def generate(notification, target_dir):
+ """
+ Generate all advisories in NOTIFICATION as text files
+ in TARGET_DIR. If TARGET_DIR is None, the advisory texts
+ will be written to the standard output.
+ """
+
+ for metadata in notification:
+ if target_dir is None:
+ __write_advisory(metadata, sys.stdout)
+ continue
+
+ filename = metadata.tracking_id + '-advisory.txt'
+ with open(os.path.join(target_dir, filename), 'wt') as fd:
+ __write_advisory(metadata, fd)
Propchange: subversion/trunk/tools/dist/security/adviser.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: subversion/trunk/tools/dist/security/parser.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/dist/security/parser.py?rev=1693501&view=auto
==============================================================================
--- subversion/trunk/tools/dist/security/parser.py (added)
+++ subversion/trunk/tools/dist/security/parser.py Thu Jul 30 20:57:04 2015
@@ -0,0 +1,177 @@
+#
+# 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.
+#
+
+"""
+Parser for CVE/CAN advisories and patches.
+"""
+
+from __future__ import absolute_import
+
+
+import os
+import ast
+import base64
+import quopri
+
+
+class Notification(object):
+ """
+ The complete security notification, containing multiple advisories.
+ """
+
+ class __Metadata(object):
+ """
+ The metadata for one advisory, with the following fields:
+ tracking_id - the CVE/CAN number
+ title - a short description of the issue
+ culprit - server, client or both
+ advisory - an Advisory object
+ patches - a list of Patch objects, sorted in descending
+ order by the base version
+ """
+
+ CULPRIT_SERVER = 'server'
+ CULPRIT_CLIENT = 'client'
+ CULPRIT_BOTH = 'both'
+
+ __culprits = frozenset((CULPRIT_SERVER, CULPRIT_CLIENT, CULPRIT_BOTH))
+
+ def __init__(self, basedir, tracking_id,
+ title, culprit, advisory, patches):
+ if culprit not in self.__culprits:
+ raise ValueError('Culprit should be one of: '
+ + ', '.join(self.__culprits))
+ self.tracking_id = tracking_id
+ self.title = title
+ self.culprit = culprit
+ self.advisory = Advisory(os.path.join(basedir, advisory))
+ self.patches = []
+ for base_version, patchfile in patches.items():
+ patch = Patch(base_version, os.path.join(basedir, patchfile))
+ self.patches.append(patch)
+ self.patches.sort(reverse=True,
+ key=lambda x: tuple(
+ int(q) for q in x.base_version.split('.')))
+
+ def __init__(self, rootdir, *tracking_ids):
+ """
+ Create the security notification for all TRACKING_IDS.
+ The advisories and patches for each tracking ID must be
+ in the appropreiately named subdirectory of ROOTDIR.
+ """
+
+ self.__advisories = []
+ for tid in tracking_ids:
+ self.__advisories.append(self.__parse_advisory(rootdir, tid))
+
+ def __iter__(self):
+ return self.__advisories.__iter__()
+
+ def __parse_advisory(self, rootdir, tracking_id):
+ basedir = os.path.join(rootdir, tracking_id)
+ with open(os.path.join(basedir, 'metadata'), 'rt') as md:
+ metadata = ast.literal_eval(md.read())
+
+ return self.__Metadata(basedir, tracking_id,
+ metadata['title'],
+ metadata['culprit'],
+ metadata['advisory'],
+ metadata['patches'])
+
+
+class __Part(object):
+ def __init__(self, path):
+ self.__text = self.__load_file(path)
+
+ def __load_file(self, path):
+ """
+ Load a file at PATH into memory as an array of lines.
+ if self.TEXTMODE is True, strip whitespace from the end of
+ all lines and strip empty lines from the end of the file.
+ """
+
+ text = []
+ with open(path, 'rb') as src:
+ for line in src:
+ if self.TEXTMODE:
+ line = line.rstrip() + b'\n'
+ text.append(line)
+
+ # Strip trailing empty lines in text mode
+ if self.TEXTMODE:
+ while len(text) and not text[-1]:
+ del text[-1]
+
+ return b''.join(text)
+
+ def get_text(self):
+ """
+ Return the raw contents.
+ """
+
+ return self.__text.decode('UTF-8')
+
+ def get_quoted_printable(self):
+ """
+ Return contents encoded as quoted-printable.
+ """
+
+ return quopri.encodestring(self.__text).decode('ascii')
+
+ BASE64_LINE_LENGTH = 64
+ def get_base64(self):
+ """
+ Return multi-line Base64-encoded contents with the lenght
+ of the lines limited to BASE64_LINE_LENGTH.
+ """
+
+ text = []
+ data = base64.standard_b64encode(self.__text)
+ start = 0
+ end = self.BASE64_LINE_LENGTH
+ while end < len(data):
+ text.append(data[start:end] + b'\n')
+ start += self.BASE64_LINE_LENGTH
+ end += self.BASE64_LINE_LENGTH
+ if start < len(data):
+ text.append(data[start:] + b'\n')
+ return b''.join(text).decode('ascii')
+
+
+class Advisory(__Part):
+ """
+ In-memory container for the text of the advisory.
+ """
+
+ TEXTMODE = True
+
+
+class Patch(__Part):
+ """
+ In-memory container for patches.
+ """
+
+ TEXTMODE = False
+
+ def __init__(self, base_version, path):
+ super(Patch, self).__init__(path)
+ self.base_version = base_version
+
+ def get_quoted_printable(self):
+ raise NotImplementedError('Quoted-printable patches? Really?')
Propchange: subversion/trunk/tools/dist/security/parser.py
------------------------------------------------------------------------------
svn:eol-style = native