Merge ~techalchemy/ubuntu-security-tools:feature/python3-migration into ubuntu-security-tools:master

Proposed by Dan Ryan
Status: Needs review
Proposed branch: ~techalchemy/ubuntu-security-tools:feature/python3-migration
Merge into: ubuntu-security-tools:master
Diff against target: 2788 lines (+994/-686)
39 files modified
audits/pk-list (+12/-10)
audits/test/data/encryption/ex_twisted.py (+1/-0)
audits/test/test-mir-code-audit.py (+2/-1)
audits/uaudit (+24/-15)
build-tools/buildlog-compare (+18/-14)
build-tools/diff-reorder (+5/-3)
build-tools/umt (+105/-102)
bzr-tools/plugins/uct-check_syntax.py (+10/-3)
bzr-tools/plugins/uct-check_syntax_full.py (+11/-3)
p-l-p/bug-status.py (+30/-23)
p-l-p/cookies-sql2txt.py (+23/-18)
p-l-p/dump-bug.py (+37/-30)
p-l-p/in-english.py (+28/-21)
p-l-p/is-cache.py (+25/-18)
p-l-p/is-private.py (+48/-20)
p-l-p/is-support.py (+21/-15)
p-l-p/not-a-bug.py (+56/-28)
p-l-p/not-private.py (+4/-3)
p-l-p/not-security-in-dups.py (+11/-8)
p-l-p/not-security-without-comment.py (+48/-20)
p-l-p/not-security.py (+49/-20)
p-l-p/reassign.py (+24/-22)
p-l-p/requires-trojaned-account.py (+2/-1)
p-l-p/set-package.py (+3/-2)
p-l-p/sub-security.py (+20/-13)
p-l-p/tag.py (+3/-2)
p-l-p/unsub-in-dups.py (+5/-4)
package-tools/debcompare (+28/-23)
repo-tools/count-hardened-pkgs (+74/-56)
repo-tools/count-hash-sources (+65/-52)
repo-tools/for-archive-tools/has-execstack (+30/-15)
repo-tools/for-archive-tools/has-symbols (+30/-15)
repo-tools/packages-support-status (+61/-52)
repo-tools/universe-binaries-with-sources-in-main.py (+7/-3)
snaps/coverity-ubuntu-security/files/bin/refresh-auth-key (+2/-1)
utilities/build_failures.py (+20/-14)
utilities/ceviche (+40/-25)
utilities/maildir2mbox.py (+10/-9)
utilities/mugshot (+2/-2)
Reviewer Review Type Date Requested Status
Alex Murray Pending
Review via email: mp+378297@code.launchpad.net

Commit message

Add support for python 3 across UST

- Add general compatibility for python 3 across UST
- Code convention & minor performance cleanup
- Safer file handle management
- str/bytes consciousness at the filesystem layer

Description of the change

Add python 3 support and minor implementation tweaks described below

- Begin implementing context managers around file handles
- Begin moving unguarded execution blocks to `if __name__ == '__main__'
    blocks in case code is ever used as a library (i.e. imported)
- Change print statements to functions
- Use `contextlib.closing` context manager around `urlopen` calls to
    ensure connections are closed
- Add alternative config parser using `ConfigParser` by adding a dummy
    section header when loading config files to avoid `configobj`
- Prefer to decompress remote gzipped content in memory to avoid writing
    to disk
- `yaml.safe_load` and `yaml.safe.dump` instead of `load` and `dump

To post a comment you must log in.
ceede68... by Dan Ryan

syntax fixes

Signed-off-by: Dan Ryan <email address hidden>

Unmerged commits

ceede68... by Dan Ryan

syntax fixes

Signed-off-by: Dan Ryan <email address hidden>

41f83d0... by Dan Ryan

Merge branch 'master' into feature/python3-migration

16ad204... by Dan Ryan

Add support for python 3 across UST

- Begin implementing context managers around file handles
- Begin moving unguarded execution blocks to `if __name__ == '__main__'
  blocks
- Change print statements to functions
- Use `contextlib.closing` context manager around `urlopen` calls to
  ensure connections are closed
- Add alternative config parser using `ConfigParser` by adding a dummy
  section header when loading config files to avoid `configobj`
- Prefer to decompress remote gzipped content in memory to avoid writing
  to disk
- `yaml.safe_load` and `yaml.safe.dump` instead of `load` and `dump`

Signed-off-by: Dan Ryan <email address hidden>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/audits/pk-list b/audits/pk-list
2index 48857b5..8faa81a 100755
3--- a/audits/pk-list
4+++ b/audits/pk-list
5@@ -1,4 +1,4 @@
6-#!/usr/bin/python3
7+#!/usr/bin/env python3
8
9 # Author: Jamie Strandboge <jamie@ubuntu.com>
10 # Copyright 2012 Canonical Ltd.
11@@ -13,7 +13,7 @@
12 # TODO: Drop privs
13 # TODO: add dbus reporting
14
15-from __future__ import print_function
16+from __future__ import absolute_import, print_function
17
18 import glob
19 import optparse
20@@ -71,12 +71,12 @@ def get_merged_mapping(default_mapping, package_mapping, local_mapping):
21 pat = re.compile(r'%s' % k)
22 for i in merged.keys():
23 if pat.search(i):
24- if i not in merged.keys():
25+ if i not in merged:
26 continue
27 merged[i]['overrides']['package'] = package_mapping[k]
28 #print "%s: %s" % (k, merged[i]['overrides']['package'])
29 else:
30- if k not in merged.keys():
31+ if k not in merged:
32 continue
33 merged[k]['overrides']['package'] = package_mapping[k]
34 #print "%s: %s" % (k, merged[k]['overrides']['package'])
35@@ -221,10 +221,10 @@ class PKOverridesHandler():
36 entries[key] = value
37 _debug(entries)
38
39- if "action" not in entries.keys():
40+ if "action" not in entries:
41 _warn("No action for '%s'. Skipping" % (section))
42 continue
43- elif "identity" not in entries.keys():
44+ elif "identity" not in entries:
45 _warn("No identity for '%s'. Skipping" % (section))
46 continue
47
48@@ -260,7 +260,7 @@ class PKActionsHandler(xml.sax.handler.ContentHandler):
49 def startElement(self, name, attrs):
50 _debug("PKActionsHandler.startElement()")
51 if name == "action":
52- if 'id' in attrs.keys():
53+ if 'id' in attrs:
54 self.curr_action = attrs['id']
55 self.mapping[self.curr_action] = dict()
56 self.in_action = True
57@@ -276,11 +276,11 @@ class PKActionsHandler(xml.sax.handler.ContentHandler):
58 return
59
60 if re.search(r'_?description', name):
61- if 'xml:lang' not in attrs.keys(): # skip translated strings
62+ if 'xml:lang' not in attrs: # skip translated strings
63 self.in_description = True
64 self.mapping[self.curr_action]['description'] = ""
65 elif re.search(r'_?message', name):
66- if 'xml:lang' not in attrs.keys(): # skip translated strings
67+ if 'xml:lang' not in attrs: # skip translated strings
68 self.in_message = True
69 self.mapping[self.curr_action]['message'] = ""
70 elif name == "defaults":
71@@ -353,7 +353,9 @@ class PKActionsHandler(xml.sax.handler.ContentHandler):
72
73 cleaned_lines = []
74 skipped_doctype = False
75- for line in open(f).readlines():
76+ with open(f, "r") as fh:
77+ lines = fh.readlines()
78+ for line in lines:
79 if line.startswith("<policyconfig>"):
80 skipped_doctype = True
81 if not skipped_doctype:
82diff --git a/audits/test/data/encryption/ex_twisted.py b/audits/test/data/encryption/ex_twisted.py
83index 891ca40..6a112a9 100644
84--- a/audits/test/data/encryption/ex_twisted.py
85+++ b/audits/test/data/encryption/ex_twisted.py
86@@ -1,2 +1,3 @@
87+from __future__ import absolute_import, print_function
88 from twisted.internet import reactor, ssl
89 from twisted.internet.ssl import ClientContextFactory
90diff --git a/audits/test/test-mir-code-audit.py b/audits/test/test-mir-code-audit.py
91index 4e6d548..73d8b6b 100755
92--- a/audits/test/test-mir-code-audit.py
93+++ b/audits/test/test-mir-code-audit.py
94@@ -147,7 +147,8 @@ class T(unittest.TestCase):
95 for line in report.splitlines():
96 if '%s:' % f in line:
97 content += pat.sub('<path>', line) + "\n"
98- open(tmp, 'w').write(content)
99+ with open(tmp, 'w') as fh:
100+ fh.write(content)
101
102 # now compare each test result
103 results = glob.glob("%s/*.result" % self.tmpdir)
104diff --git a/audits/uaudit b/audits/uaudit
105index 2ef95cf..1da183b 100755
106--- a/audits/uaudit
107+++ b/audits/uaudit
108@@ -1,4 +1,4 @@
109-#!/usr/bin/python3
110+#!/usr/bin/env python3
111
112 # Author: Jamie Strandboge <jamie@ubuntu.com>
113 # Copyright 2012 Canonical Ltd.
114@@ -16,7 +16,8 @@
115 # along with this program. If not, see <http://www.gnu.org/licenses/>.
116 #
117
118-from __future__ import print_function
119+from __future__ import absolute_import, print_function
120+
121 from pathlib import Path
122 import glob
123 import optparse
124@@ -28,6 +29,11 @@ import sys
125 import tempfile
126 import json
127
128+if sys.version_info[0] == 2:
129+ raw_input = raw_input
130+else:
131+ raw_input = input
132+
133 DEBUGGING = False
134 ust = {}
135
136@@ -125,13 +131,14 @@ def parse_package_details():
137 global ust
138 details = {}
139
140+ header = []
141 try:
142- changelog = open('debian/changelog', 'r')
143+ with open("debian/changelog", "r") as fh:
144+ # Extract info from the first line of the changelog
145+ header = fh.readline().split()
146 except Exception:
147 error("Could not open debian/changelog. Aborting.")
148
149- # Extract info from the first line of the changelog
150- header = changelog.readline().split()
151 details['package'] = header[0]
152 details['version'] = header[1].strip('()')
153
154@@ -186,7 +193,7 @@ def find_build_log(pkg, ver):
155 latest = None
156 modified = None
157 for log in glob.glob(ust['pkgbuild_logs'] + '/*%s_*' % (pkg)):
158- with open(log) as log_file:
159+ with open(log, "r") as log_file:
160 line = log_file.readline()
161 if not line.startswith("Automatic build of"):
162 # Skip the first three lines of the file for newer sbuild
163@@ -213,8 +220,10 @@ def load_ust_config(config_file):
164
165 config = {}
166 # no python-configobj in python3 yet, so read what we need ourselves
167- for line in open(config_file, 'r').readlines():
168- line.strip()
169+ with open(config_file, "r") as fh:
170+ lines = fh.readlines()
171+ for line in lines:
172+ line = line.strip()
173 if line.startswith('#'):
174 continue
175 if '=' not in line:
176@@ -264,7 +273,7 @@ def query_user_consent_to_install_tool(tool):
177
178 while True:
179 sys.stdout.write(question)
180- choice = input().lower()
181+ choice = raw_input().lower()
182 if choice in valid_answers:
183 return valid_answers[choice]
184 else:
185@@ -623,8 +632,8 @@ def cov_analyze(covdir):
186 return
187
188 # parse auth token for host and port
189- fp = open(cov_auth_key, 'r')
190- token = json.load(fp)
191+ with open(cov_auth_key, 'r') as fp:
192+ token = json.load(fp)
193 host = token['comments']['host']
194 port = token['comments']['port']
195 project = details['package']
196@@ -1013,13 +1022,13 @@ if __name__ == "__main__":
197 if not os.path.exists(txt):
198 msg("Creating %s" % txt)
199 # replace variables in file
200- s = open(base).read()
201+ with open(base, "r") as fh:
202+ s = fh.read()
203 s = s.replace('$package', package)
204 s = s.replace('$version', details['version'])
205 s = s.replace('$release', details['release'])
206- f = open(txt, 'w')
207- f.write(s)
208- f.close()
209+ with open(txt, "w") as f:
210+ f.write(s)
211 else:
212 warn("Could not find %s template to copy" % base)
213
214diff --git a/build-tools/buildlog-compare b/build-tools/buildlog-compare
215index 6225e69..697e3c1 100755
216--- a/build-tools/buildlog-compare
217+++ b/build-tools/buildlog-compare
218@@ -1,15 +1,18 @@
219-#!/usr/bin/python
220+#!/usr/bin/env python3
221 # Copyright (C) 2007-2018 Canonical, Ltd.
222 # Author: Kees Cook <kees@ubuntu.com>
223 # Jamie Strandboge <jamie@canonical.com>
224 # Marc Deslauriers <marc.deslauriers@canonical.com>
225 # License: GPLv3
226+from __future__ import absolute_import, print_function
227 import sys, subprocess, tempfile, re, os
228 import optparse
229
230 def pull_build_log(filename):
231 sep = '\nChecking correctness of source dependencies...\n'
232- log = file(filename).read()
233+ log = ""
234+ with open(filename, "r") as fh:
235+ log = fh.read()
236 if sep in log:
237 log = log.split(sep,1)[1]
238 # log = sep.join(log.split(sep)[:-1])+"\n"
239@@ -345,15 +348,16 @@ newlog = re.sub('\[[ 0-9]{4}/[ 0-9]{4}\] Linking', '[XXXX/XXXX] Linking', newlog
240 oldlog = re.sub('-DBUILD_DATETIME=...?[0-9]{4}-[0-9]{2}-[0-9]{2}T1?\d:[0-5]\d:[0-5]\d...? ', '-DBUILD_DATETIME=0000-00-00T00:00:00 ', oldlog)
241 newlog = re.sub('-DBUILD_DATETIME=...?[0-9]{4}-[0-9]{2}-[0-9]{2}T1?\d:[0-5]\d:[0-5]\d...? ', '-DBUILD_DATETIME=0000-00-00T00:00:00 ', newlog)
242
243-# Write out the files for diffing
244-oldfile = tempfile.NamedTemporaryFile(prefix='buildlog-')
245-write_log(oldfile, oldlog, opt.deparallel, pkg)
246-
247-newfile = tempfile.NamedTemporaryFile(prefix='buildlog-')
248-write_log(newfile, newlog, opt.deparallel, pkg)
249-
250-# To debug, useful to do:
251-#shutil.copy(oldfile.name, "/tmp/old")
252-#shutil.copy(newfile.name, "/tmp/new")
253-
254-subprocess.call(['diff', '-ub', oldfile.name, newfile.name])
255+# Write out the files for diffing, use context managers to clean up temporary
256+# file handles & avoid leaking handles at interpreter exit
257+with tempfile.NamedTemporaryFile(prefix='buildlog-', mode="w+") as oldfile:
258+ write_log(oldfile, oldlog, opt.deparallel, pkg)
259+ oldfile.flush()
260+
261+ with tempfile.NamedTemporaryFile(prefix='buildlog-', mode="w+") as newfile:
262+ write_log(newfile, newlog, opt.deparallel, pkg)
263+ newfile.flush()
264+ subprocess.call(['diff', '-ub', oldfile.name, newfile.name])
265+ # To debug, useful to do:
266+ #shutil.copy(oldfile.name, "/tmp/old")
267+ #shutil.copy(newfile.name, "/tmp/new")
268diff --git a/build-tools/diff-reorder b/build-tools/diff-reorder
269index ef641de..2a9232f 100755
270--- a/build-tools/diff-reorder
271+++ b/build-tools/diff-reorder
272@@ -1,4 +1,4 @@
273-#!/usr/bin/python
274+#!/usr/bin/env python3
275 # This tool attempts to balance out +/- lines in the same or
276 # adjacent diff chunks. When doing parallel builds, output
277 # frequently gets out of order, needlessly filling the diff
278@@ -8,6 +8,8 @@
279 #
280 # Copyright (C) 2009, Canonical Ltd.
281 # Author: Kees Cook <kees@ubuntu.com>
282+from __future__ import absolute_import, print_function
283+
284 import sys
285
286 def plus_minus_lines(hunk):
287@@ -99,7 +101,7 @@ class Hunk(object):
288 for line in self.lines:
289 if line.startswith('@@') and self.is_empty():
290 break
291- print prefix + line
292+ print(prefix + line)
293
294 collector = ""
295 minus = 0
296@@ -130,7 +132,7 @@ for line in sys.stdin:
297 plus = plus - 1
298 else:
299 # Broken patch!
300- raise ValueError, "Corrupted patch? '%s'" % (line.replace('\n',''))
301+ raise ValueError("Corrupted patch? '%s'" % (line.replace('\n','')))
302 if minus == 0 and plus == 0:
303 # HUNK FINISHED
304 counting = False
305diff --git a/build-tools/umt b/build-tools/umt
306index 400294d..69fcb93 100755
307--- a/build-tools/umt
308+++ b/build-tools/umt
309@@ -1,4 +1,4 @@
310-#!/usr/bin/python3
311+#!/usr/bin/env python3
312
313 # Author: Marc Deslauriers <marc.deslauriers@ubuntu.com>
314 # Author: Kees Cook <kees@ubuntu.com>
315@@ -845,114 +845,119 @@ def cmd_compare_log():
316 err("Could not find 'diff-reorder' (is '$UST' set?). Aborting.")
317 sys.exit(1)
318
319- diff = tempfile.NamedTemporaryFile(suffix='.diff',prefix='umt_compare-log_')
320- diff.write(b"Log file line counts:\n")
321- diff.flush()
322- subprocess.call(['wc','-l','--',old_logfile],stdout=diff)
323- subprocess.call(['wc','-l','--',new_logfile],stdout=diff)
324- diff.write(b"\n")
325- diff.flush()
326- command = [buildlog_compare]
327- if opt.deparallel:
328+ with tempfile.NamedTemporaryFile(
329+ suffix='.diff',prefix='umt_compare-log_', mode='w+b'
330+ ) as diff, tempfile.NamedTemporaryFile(
331+ suffix='.diff', prefix='umt_compare-log_', mode='w+b'
332+ ) as patches_diff:
333+ diff.write(b"Log file line counts:\n")
334+ diff.flush()
335+ subprocess.check_call(['wc','-l','--',old_logfile], stdout=diff)
336+ subprocess.check_call(['wc','-l','--',new_logfile], stdout=diff)
337+ diff.write(b"\n")
338+ diff.flush()
339+ command = [buildlog_compare]
340+ if opt.deparallel:
341+ if opt.balance:
342+ err("Skipping --balance with --deparallel")
343+ else:
344+ command.append("--deparallel")
345+ command += [details['package'],details['base_version_prior'],old_logfile,details['base_version'],new_logfile]
346+
347 if opt.balance:
348- err("Skipping --balance with --deparallel")
349+ p1 = subprocess.Popen(command, stdout=subprocess.PIPE)
350+ p2 = subprocess.Popen([diff_reorder], stdin=p1.stdout, stdout=subprocess.PIPE)
351+ diff.write(p2.communicate()[0])
352 else:
353- command.append("--deparallel")
354- command += [details['package'],details['base_version_prior'],old_logfile,details['base_version'],new_logfile]
355-
356- if opt.balance:
357- p1 = subprocess.Popen(command, stdout=subprocess.PIPE)
358- p2 = subprocess.Popen([diff_reorder], stdin=p1.stdout, stdout=subprocess.PIPE)
359- diff.write(p2.communicate()[0])
360- else:
361- if subprocess.call(command,stdout=diff) != 0:
362- sys.exit(1)
363+ try:
364+ subprocess.check_call(command, stdout=diff)
365+ except subprocess.CalledProcessError as e:
366+ sys.exit(e.returncode)
367+ diff.flush()
368
369- diff.flush()
370- if opt.dump_to_stdout:
371- subprocess.call(['cat', diff.name])
372- return
373+ if opt.dump_to_stdout:
374+ subprocess.call(['cat', diff.name])
375+ return
376
377- editor = os.getenv('EDITOR') or 'vi'
378- if editor.find('vi') >= 0:
379- subprocess.call([editor,'+set syntax=diff', diff.name])
380- else:
381- subprocess.call([editor, diff.name])
382+ editor = os.getenv('EDITOR') or 'vi'
383+ if editor.find('vi') >= 0:
384+ subprocess.call([editor,'+set syntax=diff', diff.name])
385+ else:
386+ subprocess.call([editor, diff.name])
387
388- # Show what is happening to the patches in a build
389- patches_diff = tempfile.NamedTemporaryFile(suffix='.diff',prefix='umt_compare-log_')
390- patches_diff.write(b"Listing of applied patch differences:\n")
391- patches_diff.write(("%s\n%s\n" % (old_logfile, new_logfile)).encode())
392- patches_diff.flush()
393+ # Show what is happening to the patches in a build
394+ patches_diff.write(b"Listing of applied patch differences:\n")
395+ patches_diff.write(("%s\n%s\n" % (old_logfile, new_logfile)).encode())
396+ patches_diff.flush()
397
398- (rc, report) = runcmd(['what-patch'])
399- if rc != 0:
400- err("running 'what-patch':\n%s" % report)
401- sys.exit(1)
402- patch_system = report.strip()
403-
404- if patch_system == "quilt" or patch_system == "dpatch" or patch_system == "cdbs":
405- patch_list = []
406- skip = False
407- if patch_system == "cdbs":
408- fn = "debian/patches"
409- if not os.path.isdir(fn):
410- patches_diff.write(("Skipping checks for patch system '%s', missing '%s'" % (patch_system, fn)).encode())
411- skip = True
412+ (rc, report) = runcmd(['what-patch'])
413+ if rc != 0:
414+ err("running 'what-patch':\n%s" % report)
415+ sys.exit(1)
416+ patch_system = report.strip()
417+
418+ if patch_system == "quilt" or patch_system == "dpatch" or patch_system == "cdbs":
419+ patch_list = []
420+ skip = False
421+ if patch_system == "cdbs":
422+ fn = "debian/patches"
423+ if not os.path.isdir(fn):
424+ patches_diff.write(("Skipping checks for patch system '%s', missing '%s'" % (patch_system, fn)).encode())
425+ skip = True
426+ else:
427+ for p in os.listdir(fn):
428+ if p.endswith('.patch') or p.endswith('.diff'):
429+ patch_list.append(".".join(p.split('.')[:-1]))
430+ else:
431+ patch_list.append(p)
432+ patch_list.sort()
433 else:
434- for p in os.listdir(fn):
435- if p.endswith('.patch') or p.endswith('.diff'):
436- patch_list.append(".".join(p.split('.')[:-1]))
437- else:
438- patch_list.append(p)
439- patch_list.sort()
440- else:
441- if patch_system == "quilt":
442- fn = "debian/patches/series.in"
443+ if patch_system == "quilt":
444+ fn = "debian/patches/series.in"
445+ if not os.path.exists(fn):
446+ fn = "debian/patches/series"
447+ elif patch_system == "dpatch":
448+ fn = "debian/patches/00list"
449+
450 if not os.path.exists(fn):
451- fn = "debian/patches/series"
452- elif patch_system == "dpatch":
453- fn = "debian/patches/00list"
454+ patches_diff.write(("Skipping checks for patch system '%s', missing '%s'" % (patch_system, fn)).encode())
455+ skip = True
456+ else:
457+ for p in open(fn).readlines():
458+ p = p.strip()
459+ if p.startswith('#') or re.search(r'^\s*$', p):
460+ continue
461+ if p.endswith('.dpatch') or p.endswith('.patch') or p.endswith('.diff'):
462+ patch_list.append(".".join(p.split('.')[:-1]))
463+ else:
464+ patch_list.append(p)
465+ if not skip:
466+ tmp = ""
467+ for p in patch_list:
468+ (rc, report) = runcmd(['egrep', p, diff.name])
469
470- if not os.path.exists(fn):
471- patches_diff.write(("Skipping checks for patch system '%s', missing '%s'" % (patch_system, fn)).encode())
472- skip = True
473- else:
474- for p in open(fn).readlines():
475- p = p.strip()
476- if p.startswith('#') or re.search(r'^\s*$', p):
477- continue
478- if p.endswith('.dpatch') or p.endswith('.patch') or p.endswith('.diff'):
479- patch_list.append(".".join(p.split('.')[:-1]))
480- else:
481- patch_list.append(p)
482- if not skip:
483- tmp = ""
484- for p in patch_list:
485- (rc, report) = runcmd(['egrep', p, diff.name])
486+ tmp = ""
487+ for line in report.splitlines():
488+ if patch_system == "cdbs" and re.search(r'^[+-]patches: ', line):
489+ continue
490+ if (line.startswith('-') or line.startswith('+')) and not re.search(r'^[+-] ', line):
491+ if line.startswith('+revert'):
492+ tmp += "# UMT WARNING: This patch was reverted. This may be a problem.\n"
493+ tmp += "# UMT WARNING: Check debian/rules and/or README.source for details.\n"
494+ tmp += line + "\n"
495
496- tmp = ""
497- for line in report.splitlines():
498- if patch_system == "cdbs" and re.search(r'^[+-]patches: ', line):
499+ if tmp == "":
500 continue
501- if (line.startswith('-') or line.startswith('+')) and not re.search(r'^[+-] ', line):
502- if line.startswith('+revert'):
503- tmp += "# UMT WARNING: This patch was reverted. This may be a problem.\n"
504- tmp += "# UMT WARNING: Check debian/rules and/or README.source for details.\n"
505- tmp += line + "\n"
506-
507- if tmp == "":
508- continue
509- else:
510- patches_diff.write(("\n%s:" % (p)).encode())
511- patches_diff.write(tmp.encode())
512- else:
513- patches_diff.write(("Skipping checks for patch system '%s'" % (patch_system)).encode())
514- patches_diff.flush()
515- if editor.find('vi') >= 0:
516- subprocess.call([editor,'+set syntax=diff', patches_diff.name])
517- else:
518- subprocess.call([editor, patches_diff.name])
519+ else:
520+ patches_diff.write(("\n%s:" % (p)).encode())
521+ patches_diff.write(tmp.encode())
522+ else:
523+ patches_diff.write(("Skipping checks for patch system '%s'" % (patch_system)).encode())
524+ patches_diff.flush()
525+ if editor.find('vi') >= 0:
526+ subprocess.call([editor,'+set syntax=diff', patches_diff.name])
527+ else:
528+ subprocess.call([editor, patches_diff.name])
529
530 def cmd_compare_bin():
531 '''Compare binary debs with prior version'''
532@@ -1352,10 +1357,8 @@ Acquire::Languages "none";
533 continue
534
535 path = os.path.join(reports_dest, b + '_' + arch + '.txt')
536- handle = open(path,"w+")
537- handle.write(report)
538- handle.flush()
539- handle.close()
540+ with open(path,"w+") as handle:
541+ handle.write(report)
542
543 if opt.edit:
544 editor = os.getenv('EDITOR') or 'vi'
545diff --git a/bzr-tools/plugins/uct-check_syntax.py b/bzr-tools/plugins/uct-check_syntax.py
546index c22e3a7..5f7b56c 100644
547--- a/bzr-tools/plugins/uct-check_syntax.py
548+++ b/bzr-tools/plugins/uct-check_syntax.py
549@@ -1,13 +1,20 @@
550-#!/usr/bin/python
551+#!/usr/bin/env python3
552
553-from __future__ import print_function
554+from __future__ import absolute_import, print_function
555+
556+import sys
557
558 from bzrlib.branch import Branch
559
560+if sys.version_info[0] == 2:
561+ raw_input = raw_input
562+else:
563+ raw_input = input
564+
565 def run_tests(local, master, old_revno, old_revid, new_revno, new_revid, tree_delta, new_tree):
566 # print(local, master, old_revno, old_revid, new_revno, new_revid, tree_delta, new_tree)
567 import os
568- if 'ubuntu-cve-tracker' in master.base and not os.environ.has_key('UCT_IGNORE_CHECK_SYNTAX'):
569+ if 'ubuntu-cve-tracker' in master.base and 'UCT_IGNORE_CHECK_SYNTAX' not in os.environ:
570 import subprocess
571 print('')
572
573diff --git a/bzr-tools/plugins/uct-check_syntax_full.py b/bzr-tools/plugins/uct-check_syntax_full.py
574index a9e2d5c..14eaea0 100644
575--- a/bzr-tools/plugins/uct-check_syntax_full.py
576+++ b/bzr-tools/plugins/uct-check_syntax_full.py
577@@ -1,12 +1,20 @@
578-#!/usr/bin/python
579+#!/usr/bin/env python3
580+from __future__ import absolute_import, print_function
581+import sys
582 from bzrlib.branch import Branch
583
584+if sys.version_info[0] == 2:
585+ raw_input = raw_input
586+else:
587+ raw_input = input
588+
589+
590 def run_tests(local, master, old_revno, old_revid, new_revno, new_revid, seven, eight):
591 #print local, master, old_revno, old_revid, new_revno, new_revid, seven, eight
592 import os
593- if 'ubuntu-cve-tracker' in master.base and not os.environ.has_key('UCT_IGNORE_CHECK_SYNTAX'):
594+ if 'ubuntu-cve-tracker' in master.base and 'UCT_IGNORE_CHECK_SYNTAX' not in os.environ:
595 import subprocess
596- print ''
597+ print('')
598 rc = subprocess.call(['./scripts/check-syntax','--verbose'])
599 if rc != 0:
600 import sys
601diff --git a/p-l-p/bug-status.py b/p-l-p/bug-status.py
602index 16cbfbf..55c0bc9 100755
603--- a/p-l-p/bug-status.py
604+++ b/p-l-p/bug-status.py
605@@ -1,36 +1,43 @@
606-#!/usr/bin/env python
607+#!/usr/bin/env python3
608 # Copyright (C) 2007 Canonical, Ltd. Jamie Strandboge <jamie@ubuntu.com>
609 # License: GPLv2
610 # Change the cookie path below...
611-import sys
612+from __future__ import absolute_import, print_function
613+
614 import os
615+import sys
616 import launchpadbugs.connector as Connector
617
618-Bug = Connector.ConnectBug()
619-Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
620
621-for num in sys.argv[1:]:
622- bug = Bug(int(num))
623+def print_bugs():
624+ Bug = Connector.ConnectBug()
625+ Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
626+
627+ for num in sys.argv[1:]:
628+ bug = Bug(int(num))
629+
630+ print(num + ": " + bug.title)
631+ print(" URL: https://bugs.launchpad.net/bugs/" + num)
632+ if bug.duplicate_of:
633+ print(" Duplicate of " + str(bug.duplicate_of))
634+ print(" Status: " + bug.status + "\n")
635
636- print num + ": " + bug.title
637- print " URL: https://bugs.launchpad.net/bugs/" + num
638- if bug.duplicate_of:
639- print " Duplicate of " + str(bug.duplicate_of)
640- print " Status: " + bug.status + "\n"
641+ if bug.private:
642+ print(" Private: yes")
643+ else:
644+ print(" Private: no")
645
646- if bug.private:
647- print " Private: yes"
648- else:
649- print " Private: no"
650+ if bug.security:
651+ print(" Security: yes")
652+ else:
653+ print(" Security: no")
654
655- if bug.security:
656- print " Security: yes"
657- else:
658- print " Security: no"
659+ print("\n Subscribers:")
660+ for s in bug.subscribers:
661+ print(" " + s)
662
663- print "\n Subscribers:"
664- for s in bug.subscribers:
665- print " " + s
666+ print("\n--\n")
667
668- print "\n--\n"
669
670+if __name__ == "__main__":
671+ print_bugs()
672\ No newline at end of file
673diff --git a/p-l-p/cookies-sql2txt.py b/p-l-p/cookies-sql2txt.py
674index cc6347c..304ca01 100755
675--- a/p-l-p/cookies-sql2txt.py
676+++ b/p-l-p/cookies-sql2txt.py
677@@ -1,33 +1,38 @@
678-#!/usr/bin/python
679+#!/usr/bin/env python3
680 # Extract.cookies.sqlite.txt-like file from FF3 sqlite3 cookies file. e.g.:
681 # .cookies.sqlite-sql2txt ~/.mozilla/*/cookies.sqlite launchpad
682 #
683 # Copyright 2008 Kees Cook <kees@outflux.net>
684 # License: GPLv2
685-import sys
686+from __future__ import absolute_import, print_function
687 import os
688+import sys
689+# TODO: Migrate this to sqlite3 + argparse
690 from pysqlite2 import dbapi2 as sqlite
691
692-def usage():
693- print >>sys.stderr, "Usage: %s SQLITE3DB DOMAIN"
694+def print_usage():
695+ print("Usage: %s SQLITE3DB DOMAIN", file=sys.stderr)
696 sys.exit(1)
697
698-try:
699- filename = sys.argv[1]
700- match = '%%%s%%' % sys.argv[2]
701-except:
702- usage()
703
704-con = sqlite.connect(filename)
705+def print_cookie_file(filename, match_param):
706+ match = '%%%s%%' % match_param
707+ con = sqlite.connect(filename)
708+
709+ cur = con.cursor()
710+ cur.execute("select host, path, isSecure, expiry, name, value from moz.cookies.sqlite where host like ?", [match])
711
712-cur = con.cursor()
713-cur.execute("select host, path, isSecure, expiry, name, value from moz.cookies.sqlite where host like ?", [match])
714+ ftstr = ["FALSE","TRUE"]
715
716-ftstr = ["FALSE","TRUE"]
717+ print('# HTTP Cookie File')
718+ for item in cur.fetchall():
719+ print("%s\t%s\t%s\t%s\t%s\t%s\t%s" % (
720+ item[0], ftstr[item[0].startswith('.')], item[1],
721+ ftstr[item[2]], item[3], item[4], item[5]))
722
723-print '# HTTP Cookie File'
724-for item in cur.fetchall():
725- print "%s\t%s\t%s\t%s\t%s\t%s\t%s" % (
726- item[0], ftstr[item[0].startswith('.')], item[1],
727- ftstr[item[2]], item[3], item[4], item[5])
728
729+if __name__ == "__main__":
730+ try:
731+ print_cookie_file(sys.argv[1], sys.argv[2])
732+ except Exception:
733+ print_usage()
734\ No newline at end of file
735diff --git a/p-l-p/dump-bug.py b/p-l-p/dump-bug.py
736index 8b7d8d6..bf2851a 100755
737--- a/p-l-p/dump-bug.py
738+++ b/p-l-p/dump-bug.py
739@@ -1,15 +1,16 @@
740-#!/usr/bin/env python
741+#!/usr/bin/env python3
742 # Copyright (C) 2007 Canonical, Ltd. Jamie Strandboge <jamie@ubuntu.com>
743 # License: GPLv2
744 # Change the cookie path below...
745+from __future__ import absolute_import, print_function
746 import sys
747 import os
748 import launchpadbugs.connector as Connector
749
750
751 def printDict(di, format="%-25s %s"):
752- for (key, val) in di.items():
753- print format % (str(key)+':', val)
754+ for key, val in di.items():
755+ print(format % (str(key)+':', val))
756
757 def dumpObj(obj, maxlen=77, lindent=24, maxspew=600):
758 """Print a nicely formatted overview of an object.
759@@ -79,7 +80,7 @@ def dumpObj(obj, maxlen=77, lindent=24, maxspew=600):
760 elif (isinstance(attr, types.MethodType) or
761 isinstance(attr, types.FunctionType)):
762 methods.append( (slot, attr) )
763- elif isinstance(attr, types.TypeType):
764+ elif isinstance(attr, type):
765 classes.append( (slot, attr) )
766 else:
767 attrs.append( (slot, attr) )
768@@ -109,57 +110,57 @@ def dumpObj(obj, maxlen=77, lindent=24, maxspew=600):
769 objclass = type(obj).__name__
770 intro = "Instance of class '%s' as defined in module %s with id %d" % \
771 (objclass, objmodule, id(obj))
772- print '\n'.join(prettyPrint(intro, maxlen))
773+ print('\n'.join(prettyPrint(intro, maxlen)))
774
775 # Object's Docstring
776 if objdoc is None:
777 objdoc = str(objdoc)
778 else:
779 objdoc = ('"""' + objdoc.strip() + '"""')
780- print
781- print prettyPrintCols( ('Documentation string:',
782+ print()
783+ print(prettyPrintCols( ('Documentation string:',
784 truncstring(objdoc, maxspew)),
785- normalwidths, ' ')
786+ normalwidths, ' '))
787
788 # Built-in methods
789 if builtins:
790 bi_str = delchars(str(builtins), "[']") or str(None)
791- print
792- print prettyPrintCols( ('Built-in Methods:',
793+ print()
794+ print(prettyPrintCols( ('Built-in Methods:',
795 truncstring(bi_str, maxspew)),
796- normalwidths, ', ')
797+ normalwidths, ', '))
798
799 # Classes
800 if classes:
801- print
802- print 'Classes:'
803+ print()
804+ print('Classes:')
805 for (classname, classtype) in classes:
806 classdoc = getattr(classtype, '__doc__', None) or '<No documentation>'
807- print prettyPrintCols( ('',
808+ print(prettyPrintCols( ('',
809 classname,
810 truncstring(classdoc, maxspew)),
811- tabbedwidths, ' ')
812+ tabbedwidths, ' '))
813
814 # User methods
815 if methods:
816- print
817- print 'Methods:'
818+ print()
819+ print('Methods:')
820 for (methodname, method) in methods:
821 methoddoc = getattr(method, '__doc__', None) or '<No documentation>'
822- print prettyPrintCols( ('',
823+ print(prettyPrintCols( ('',
824 methodname,
825 truncstring(methoddoc, maxspew)),
826- tabbedwidths, ' ')
827+ tabbedwidths, ' '))
828
829 # Attributes
830 if attrs:
831- print
832- print 'Attributes:'
833+ print()
834+ print('Attributes:')
835 for (attr, val) in attrs:
836- print prettyPrintCols( ('',
837+ print(prettyPrintCols( ('',
838 attr,
839 truncstring(str(val), maxspew)),
840- tabbedwidths, ' ')
841+ tabbedwidths, ' '))
842
843 def prettyPrintCols(strings, widths, split=' '):
844 """Pretty prints text in colums, with each string breaking at
845@@ -168,7 +169,7 @@ def prettyPrintCols(strings, widths, split=' '):
846
847 assert len(strings) == len(widths)
848
849- strings = map(nukenewlines, strings)
850+ strings = list(map(nukenewlines, strings))
851
852 # pretty print each column
853 cols = [''] * len(strings)
854@@ -179,7 +180,7 @@ def prettyPrintCols(strings, widths, split=' '):
855 format = ''.join(["%%-%ds" % width for width in widths[0:-1]]) + "%s"
856
857 def formatline(*cols):
858- return format % tuple(map(lambda s: (s or ''), cols))
859+ return format % tuple([(s or '') for s in cols])
860
861 # generate the formatted text
862 return '\n'.join(map(formatline, *cols))
863@@ -226,11 +227,17 @@ def delchars(str, chars):
864 return str.translate(identity, chars)
865
866
867-Bug = Connector.ConnectBug()
868-Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
869+# TODO: Migrate this to argparse
870
871-for num in sys.argv[1:]:
872- bug = Bug(int(num))
873+def print_bugs(*bugs):
874+ Bug = Connector.ConnectBug()
875+ Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
876
877- dumpObj(bug, 1024, 24, 1024)
878+ for num in bugs:
879+ bug = Bug(int(num))
880
881+ dumpObj(bug, 1024, 24, 1024)
882+
883+
884+if __name__ == "__main__":
885+ print_bugs(*sys.argv[1:])
886\ No newline at end of file
887diff --git a/p-l-p/in-english.py b/p-l-p/in-english.py
888index 8e56db0..ad75f5e 100755
889--- a/p-l-p/in-english.py
890+++ b/p-l-p/in-english.py
891@@ -1,30 +1,37 @@
892-#!/usr/bin/env python
893+#!/usr/bin/env python3
894 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
895 # License: GPLv2
896 # Change the cookie path below...
897-import sys
898+from __future__ import absolute_import, print_function
899 import os
900+import sys
901 import launchpadbugs.connector as Connector
902
903 Bug = Connector.ConnectBug()
904 Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
905
906-for num in sys.argv[1:]:
907- bug = Bug(num)
908-
909- try:
910- bug.subscribers.add("ubuntu-bugs")
911- except:
912- pass
913-
914- comment = Bug.NewComment(text='Thanks for taking the time to report this bug and helping to make Ubuntu better. Your bug report is more likely to get attention if it is made in English, since this is the language understood by the majority of Ubuntu developers. Additionally, please only mark a bug as "security" if it shows evidence of allowing attackers to cross privilege boundaries or to directly cause loss of data/privacy. Please feel free to report any other bugs you may find.', subject='')
915- bug.comments.add(comment)
916- bug.private = False
917- bug.security = False
918- bug.status = 'Incomplete'
919-
920- try:
921- bug.subscribers.remove("ubuntu-security")
922- except:
923- pass
924- bug.commit(force_changes=True, ignore_lp_errors=False)
925+
926+def reassign_bugs_to_bugs_queue(*bugs)
927+ for num in bugs:
928+ bug = Bug(num)
929+
930+ try:
931+ bug.subscribers.add("ubuntu-bugs")
932+ except:
933+ pass
934+
935+ comment = Bug.NewComment(text='Thanks for taking the time to report this bug and helping to make Ubuntu better. Your bug report is more likely to get attention if it is made in English, since this is the language understood by the majority of Ubuntu developers. Additionally, please only mark a bug as "security" if it shows evidence of allowing attackers to cross privilege boundaries or to directly cause loss of data/privacy. Please feel free to report any other bugs you may find.', subject='')
936+ bug.comments.add(comment)
937+ bug.private = False
938+ bug.security = False
939+ bug.status = 'Incomplete'
940+
941+ try:
942+ bug.subscribers.remove("ubuntu-security")
943+ except:
944+ pass
945+ bug.commit(force_changes=True, ignore_lp_errors=False)
946+
947+
948+if __name__ == "__main__":
949+ reassign_bugs_to_bugs_queue(*sys.argv[1:])
950\ No newline at end of file
951diff --git a/p-l-p/is-cache.py b/p-l-p/is-cache.py
952index 5aafacf..da75103 100755
953--- a/p-l-p/is-cache.py
954+++ b/p-l-p/is-cache.py
955@@ -1,29 +1,36 @@
956-#!/usr/bin/env python
957+#!/usr/bin/env python3
958 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
959 # License: GPLv2
960 # Change the cookie path below...
961+from __future__ import absolute_import, print_function
962 import sys, os
963 import launchpadbugs.connector as Connector
964
965-Bug = Connector.ConnectBug()
966-Bug.authentication = os.path.expanduser('~/.plb_cookies')
967-reply = '''Thanks for your report. We appreciate the difficulties you are facing, but this bug has already been reported. Please review the information and solution in bug 124373. We are marking this bug as a duplicate.'''
968
969-for num in sys.argv[1:]:
970- print '%s ...' % (num)
971- bug = Bug(num)
972+# TODO: Switch to argparse
973+def respond_to_duplicate_bugs(*bugs)
974+ Bug = Connector.ConnectBug()
975+ Bug.authentication = os.path.expanduser('~/.plb_cookies')
976+ reply = '''Thanks for your report. We appreciate the difficulties you are facing, but this bug has already been reported. Please review the information and solution in bug 124373. We are marking this bug as a duplicate.'''
977+ for bug in bugs:
978+ print('%s ...' % (num))
979+ bug = Bug(num)
980
981- comment = Bug.NewComment(text=reply)
982- bug.comments.add(comment)
983+ comment = Bug.NewComment(text=reply)
984+ bug.comments.add(comment)
985
986- bug.status = 'Invalid'
987- bug.private = False
988- bug.security = False
989- if not 'ubuntu-bugs' in bug.subscribers['directly']:
990- bug.subscribers.add("ubuntu-bugs")
991- if 'ubuntu-security' in bug.subscribers['directly']:
992- bug.subscribers.remove("ubuntu-security")
993+ bug.status = 'Invalid'
994+ bug.private = False
995+ bug.security = False
996+ if not 'ubuntu-bugs' in bug.subscribers['directly']:
997+ bug.subscribers.add("ubuntu-bugs")
998+ if 'ubuntu-security' in bug.subscribers['directly']:
999+ bug.subscribers.remove("ubuntu-security")
1000
1001- bug.duplicate_of = 124373
1002+ bug.duplicate_of = 124373
1003
1004- bug.commit(force_changes=True, ignore_lp_errors=False)
1005+ bug.commit(force_changes=True, ignore_lp_errors=False)
1006+
1007+
1008+if __name__ == "__main__":
1009+ respond_to_duplicate_bugs(*sys.argv[1:])
1010\ No newline at end of file
1011diff --git a/p-l-p/is-private.py b/p-l-p/is-private.py
1012index d29045b..0333f51 100755
1013--- a/p-l-p/is-private.py
1014+++ b/p-l-p/is-private.py
1015@@ -1,33 +1,61 @@
1016-#!/usr/bin/env python
1017+#!/usr/bin/env python3
1018 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1019 # License: GPLv2
1020 # Change the cookie path below...
1021 # Change lp_user to self below... (3 times)
1022-import sys
1023+from __future__ import absolute_import, print_function
1024 import os
1025+import sys
1026 import launchpadbugs.connector as Connector
1027-import configobj
1028
1029-config = configobj.ConfigObj(os.path.expanduser('~/.ubuntu-cve-tracker.conf'))
1030-Bug = Connector.ConnectBug()
1031-Bug.authentication = os.path.join(config['plb_authentication'])
1032-lp_user = config['plb_user']
1033+if sys.version_info[0] == 2:
1034+ from ConfigParser import SafeConfigParser as ConfigParser
1035+ from cStringIO import StringIO
1036+else:
1037+ from configparser import ConfigParser
1038+ from io import StringIO
1039
1040-for num in sys.argv[1:]:
1041- print '%s ... ' % (num)
1042- bug = Bug(num)
1043+UCT_PATH = os.path.expanduser('~/.ubuntu-cve-tracker.conf')
1044
1045- if not lp_user in bug.subscriptions:
1046- bug.subscribers.add(lp_user)
1047- bug.commit(force_changes=True, ignore_lp_errors=False)
1048
1049- if bug.security:
1050- bug.security = False
1051+def get_config():
1052+ # replace configobj functionality to avoid the extra dependency
1053+ config_content = StringIO()
1054+ config_content.write("[dummy_section]\n")
1055+ with open(UCT_PATH, "r") as fh:
1056+ config_content.write(fh.read())
1057+ config_content.seek(0)
1058+ parser = ConfigParser()
1059+ read_file_func = getattr(parser, "read_file", getattr(parser, "readfp", None))
1060+ if read_file_func is not None:
1061+ read_file_func(config_content)
1062+ return {k: v for k, v in parser.items("dummy_section")}
1063+
1064+
1065+def make_bugs_private(*bugs):
1066+ for num in bugs:
1067+ config = get_config()
1068+ Bug = Connector.ConnectBug()
1069+ Bug.authentication = os.path.join(config['plb_authentication'])
1070+ lp_user = config['plb_user']
1071+ print('%s ... ' % (num))
1072+ bug = Bug(num)
1073+
1074+ if not lp_user in bug.subscriptions:
1075+ bug.subscribers.add(lp_user)
1076+ bug.commit(force_changes=True, ignore_lp_errors=False)
1077+
1078+ if bug.security:
1079+ bug.security = False
1080+ bug.commit(force_changes=True, ignore_lp_errors=False)
1081+
1082+ bug = Bug(num)
1083+ if 'ubuntu-security' in bug.subscriptions:
1084+ bug.subscribers.remove("ubuntu-security")
1085+
1086+ bug.subscribers.remove(lp_user)
1087 bug.commit(force_changes=True, ignore_lp_errors=False)
1088
1089- bug = Bug(num)
1090- if 'ubuntu-security' in bug.subscriptions:
1091- bug.subscribers.remove("ubuntu-security")
1092
1093- bug.subscribers.remove(lp_user)
1094- bug.commit(force_changes=True, ignore_lp_errors=False)
1095+if __name__ == "__main__":
1096+ make_bugs_private(*sys.argv[1:])
1097\ No newline at end of file
1098diff --git a/p-l-p/is-support.py b/p-l-p/is-support.py
1099index 0dd199f..e2b9eee 100755
1100--- a/p-l-p/is-support.py
1101+++ b/p-l-p/is-support.py
1102@@ -1,25 +1,31 @@
1103-#!/usr/bin/env python
1104+#!/usr/bin/env python3
1105 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1106 # License: GPLv2
1107 # Change the cookie path below...
1108-import sys
1109+from __future__ import absolute_import, print_function
1110 import os
1111+import sys
1112 import launchpadbugs.connector as Connector
1113
1114-Bug = Connector.ConnectBug()
1115-Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
1116+def close_non_bug_reports(*bugs):
1117+ Bug = Connector.ConnectBug()
1118+ Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
1119+
1120+ for num in bugs:
1121+ bug = Bug(num)
1122+
1123+ if 'ubuntu-bugs' not in bug.subscriptions:
1124+ bug.subscribers.add("ubuntu-bugs")
1125
1126-for num in sys.argv[1:]:
1127- bug = Bug(num)
1128+ comment = Bug.NewComment(text='Thanks for your comments. This does not appear to be a bug report and we are closing it. We appreciate the difficulties you are facing, but it would make more sense to raise your question in the support tracker. Please visit https://answers.launchpad.net/ubuntu/+addquestion', subject='')
1129+ bug.comments.add(comment)
1130+ bug.status = 'Invalid'
1131+ bug.private = False
1132+ bug.security = False
1133
1134- if 'ubuntu-bugs' not in bug.subscriptions:
1135- bug.subscribers.add("ubuntu-bugs")
1136+ bug.subscribers.remove("ubuntu-security")
1137+ bug.commit(force_changes=True, ignore_lp_errors=False)
1138
1139- comment = Bug.NewComment(text='Thanks for your comments. This does not appear to be a bug report and we are closing it. We appreciate the difficulties you are facing, but it would make more sense to raise your question in the support tracker. Please visit https://answers.launchpad.net/ubuntu/+addquestion', subject='')
1140- bug.comments.add(comment)
1141- bug.status = 'Invalid'
1142- bug.private = False
1143- bug.security = False
1144
1145- bug.subscribers.remove("ubuntu-security")
1146- bug.commit(force_changes=True, ignore_lp_errors=False)
1147+if __name__ == "__main__":
1148+ close_non_bug_reports(*sys.argv[1:])
1149\ No newline at end of file
1150diff --git a/p-l-p/not-a-bug.py b/p-l-p/not-a-bug.py
1151index ad185ff..a7a30f9 100755
1152--- a/p-l-p/not-a-bug.py
1153+++ b/p-l-p/not-a-bug.py
1154@@ -1,41 +1,69 @@
1155-#!/usr/bin/env python
1156+#!/usr/bin/env python3
1157 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1158 # License: GPLv2
1159 # Change the cookie path below...
1160-import sys
1161+from __future__ import absolute_import, print_function
1162 import os
1163+import sys
1164 import launchpadbugs.connector as Connector
1165-import configobj
1166
1167-config = configobj.ConfigObj(os.path.expanduser('~/.ubuntu-cve-tracker.conf'))
1168-Bug = Connector.ConnectBug()
1169-Bug.authentication = os.path.join(config['plb_authentication'])
1170+if sys.version_info[0] == 2:
1171+ from ConfigParser import SafeConfigParser as ConfigParser
1172+ from cStringIO import StringIO
1173+else:
1174+ from configparser import ConfigParser
1175+ from io import StringIO
1176+
1177+UCT_PATH = os.path.expanduser('~/.ubuntu-cve-tracker.conf')
1178+
1179+
1180+def get_config():
1181+ # Replace configobj functionality to avoid the extra dependency
1182+ config_content = StringIO()
1183+ config_content.write("[dummy_section]\n")
1184+ with open(UCT_PATH, "r") as fh:
1185+ config_content.write(fh.read())
1186+ config_content.seek(0)
1187+ parser = ConfigParser()
1188+ read_file_func = getattr(parser, "read_file", getattr(parser, "readfp", None))
1189+ if read_file_func is not None:
1190+ read_file_func(config_content)
1191+ return {k: v for k, v in parser.items("dummy_section")}
1192+
1193+
1194+def flag_non_bugs(*bugs):
1195+ Bug = Connector.ConnectBug()
1196+ Bug.authentication = os.path.join(get_config()['plb_authentication'])
1197+
1198+ for num in bugs:
1199+ bug = Bug(num)
1200+
1201+ try:
1202+ bug.subscribers.add("ubuntu-bugs")
1203+ changed = True
1204+ except:
1205+ pass
1206
1207-for num in sys.argv[1:]:
1208- bug = Bug(num)
1209+ comment = Bug.NewComment(text='''Thank you for using Ubuntu and taking the time to report a bug. Your report should contain, at a minimum, the following information so we can better find the source of the bug and work to resolve it.
1210
1211- try:
1212- bug.subscribers.add("ubuntu-bugs")
1213- changed = True
1214- except:
1215- pass
1216+ Submitting the bug about the proper source package is essential. For help see https://wiki.ubuntu.com/Bugs/FindRightPackage . Additionally, in the report please include:
1217
1218- comment = Bug.NewComment(text='''Thank you for using Ubuntu and taking the time to report a bug. Your report should contain, at a minimum, the following information so we can better find the source of the bug and work to resolve it.
1219+ 1) The release of Ubuntu you are using, via 'cat /etc/lsb-release' or System -> About Ubuntu.
1220+ 2) The version of the package you are using, via 'dpkg -l PKGNAME | cat' or by checking in Synaptic.
1221+ 3) What happened and what you expected to happen.
1222
1223-Submitting the bug about the proper source package is essential. For help see https://wiki.ubuntu.com/Bugs/FindRightPackage . Additionally, in the report please include:
1224+ The Ubuntu community has also created debugging procedures for a wide variety of packages at https://wiki.ubuntu.com/DebuggingProcedures . Following the debugging instructions for the affected package will make your bug report much more complete. Thanks!''', subject='')
1225+ bug.comments.add(comment)
1226+ bug.status = 'Invalid'
1227+ bug.private = False
1228+ bug.security = False
1229
1230-1) The release of Ubuntu you are using, via 'cat /etc/lsb-release' or System -> About Ubuntu.
1231-2) The version of the package you are using, via 'dpkg -l PKGNAME | cat' or by checking in Synaptic.
1232-3) What happened and what you expected to happen.
1233+ try:
1234+ bug.subscribers.remove("ubuntu-security")
1235+ except:
1236+ pass
1237+ bug.commit(force_changes=True, ignore_lp_errors=False)
1238
1239-The Ubuntu community has also created debugging procedures for a wide variety of packages at https://wiki.ubuntu.com/DebuggingProcedures . Following the debugging instructions for the affected package will make your bug report much more complete. Thanks!''', subject='')
1240- bug.comments.add(comment)
1241- bug.status = 'Invalid'
1242- bug.private = False
1243- bug.security = False
1244
1245- try:
1246- bug.subscribers.remove("ubuntu-security")
1247- except:
1248- pass
1249- bug.commit(force_changes=True, ignore_lp_errors=False)
1250+if __name__ == "__main__":
1251+ flag_non_bugs(*sys.argv[1:])
1252\ No newline at end of file
1253diff --git a/p-l-p/not-private.py b/p-l-p/not-private.py
1254index 289052f..4abc231 100755
1255--- a/p-l-p/not-private.py
1256+++ b/p-l-p/not-private.py
1257@@ -1,13 +1,14 @@
1258-#!/usr/bin/env python
1259+#!/usr/bin/env python3
1260 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1261 # License: GPLv2
1262 # Change the cookie path below...
1263+from __future__ import absolute_import, print_function
1264 import sys
1265 import os
1266 import launchpadbugs.connector as Connector
1267
1268-print "This script is obsoleted by launchpadlib/not-private. Please use it instead."
1269-print "Continuing..."
1270+print("This script is obsoleted by launchpadlib/not-private. Please use it instead.")
1271+print("Continuing...")
1272
1273 Bug = Connector.ConnectBug()
1274 Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
1275diff --git a/p-l-p/not-security-in-dups.py b/p-l-p/not-security-in-dups.py
1276index 4046c74..e9a1ced 100755
1277--- a/p-l-p/not-security-in-dups.py
1278+++ b/p-l-p/not-security-in-dups.py
1279@@ -1,7 +1,8 @@
1280-#!/usr/bin/env python
1281+#!/usr/bin/env python3
1282 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1283 # License: GPLv2
1284 # Change the cookie path below...
1285+from __future__ import absolute_import, print_function
1286 import sys
1287 import os
1288 import launchpadbugs.connector as Connector
1289@@ -10,7 +11,7 @@ Bug = Connector.ConnectBug()
1290 Bug.authentication = os.path.join(os.environ['HOME'], '.cookies.sqlite')
1291
1292 def not_security(num):
1293- print '%s ...' % (num),
1294+ print('%s ...' % (num), end=' ')
1295 bug = Bug(num)
1296
1297 changed = False
1298@@ -28,13 +29,15 @@ def not_security(num):
1299 changed = True
1300 if changed:
1301 bug.commit(force_changes=True, ignore_lp_errors=False)
1302- print 'updated'
1303+ print('updated')
1304 else:
1305- print 'skipped'
1306+ print('skipped')
1307 return bug
1308
1309-for num in sys.argv[1:]:
1310- bug = not_security(num)
1311
1312- for num in bug.duplicates:
1313- not_security(num)
1314+if __name__ == "__main__":
1315+ for num in sys.argv[1:]:
1316+ bug = not_security(num)
1317+
1318+ for num in bug.duplicates:
1319+ not_security(num)
1320diff --git a/p-l-p/not-security-without-comment.py b/p-l-p/not-security-without-comment.py
1321index e890308..3dad01b 100755
1322--- a/p-l-p/not-security-without-comment.py
1323+++ b/p-l-p/not-security-without-comment.py
1324@@ -1,29 +1,57 @@
1325-#!/usr/bin/env python
1326+#!/usr/bin/env python3
1327 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1328 # License: GPLv3
1329-import sys
1330+from __future__ import absolute_import, print_function
1331 import os
1332+import sys
1333 import launchpadbugs.connector as Connector
1334-import configobj
1335
1336-config = configobj.ConfigObj(os.path.expanduser('~/.ubuntu-cve-tracker.conf'))
1337-Bug = Connector.ConnectBug()
1338-Bug.authentication = os.path.join(config['plb_authentication'])
1339+if sys.version_info[0] == 2:
1340+ from ConfigParser import SafeConfigParser as ConfigParser
1341+ from cStringIO import StringIO
1342+else:
1343+ from configparser import ConfigParser
1344+ from io import StringIO
1345+
1346+UCT_PATH = os.path.expanduser('~/.ubuntu-cve-tracker.conf')
1347+
1348+
1349+def get_config():
1350+ config_content = StringIO()
1351+ config_content.write("[dummy_section]\n")
1352+ with open(UCT_PATH, "r") as fh:
1353+ config_content.write(fh.read())
1354+ config_content.seek(0)
1355+ parser = ConfigParser()
1356+ read_file_func = getattr(parser, "read_file", getattr(parser, "readfp", None))
1357+ if read_file_func is not None:
1358+ read_file_func(config_content)
1359+ return {k: v for k, v in parser.items("dummy_section")}
1360+
1361+
1362+def remove_security_without_comment(*bugs):
1363+ Bug = Connector.ConnectBug()
1364+ config = get_config()
1365+ Bug.authentication = os.path.join(config['plb_authentication'])
1366+
1367+ for num in bugs:
1368+ print('%s ...' % (num))
1369+ bug = Bug(num)
1370+
1371+ bug.private = False
1372+ bug.security = False
1373
1374-for num in sys.argv[1:]:
1375- print '%s ...' % (num)
1376- bug = Bug(num)
1377+ try:
1378+ bug.subscribers.add("ubuntu-bugs")
1379+ except:
1380+ print("ubuntu-bugs already subscribed", file=sys.stderr)
1381
1382- bug.private = False
1383- bug.security = False
1384+ try:
1385+ bug.subscribers.remove("ubuntu-security")
1386+ except:
1387+ pass
1388+ bug.commit(force_changes=True, ignore_lp_errors=False)
1389
1390- try:
1391- bug.subscribers.add("ubuntu-bugs")
1392- except:
1393- print >>sys.stderr, "ubuntu-bugs already subscribed"
1394
1395- try:
1396- bug.subscribers.remove("ubuntu-security")
1397- except:
1398- pass
1399- bug.commit(force_changes=True, ignore_lp_errors=False)
1400+if __name__ == "__main__":
1401+ remove_security_without_comment(*sys.argv[1:])
1402\ No newline at end of file
1403diff --git a/p-l-p/not-security.py b/p-l-p/not-security.py
1404index 4108e82..c909518 100755
1405--- a/p-l-p/not-security.py
1406+++ b/p-l-p/not-security.py
1407@@ -1,31 +1,60 @@
1408-#!/usr/bin/env python
1409+#!/usr/bin/env python3
1410 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1411 # License: GPLv3
1412+from __future__ import absolute_import, print_function
1413 import sys
1414 import os
1415 import launchpadbugs.connector as Connector
1416-import configobj
1417
1418-config = configobj.ConfigObj(os.path.expanduser('~/.ubuntu-cve-tracker.conf'))
1419-Bug = Connector.ConnectBug()
1420-Bug.authentication = os.path.join(config['plb_authentication'])
1421+if sys.version_info[0] == 2:
1422+ from ConfigParser import SafeConfigParser as ConfigParser
1423+ from cStringIO import StringIO
1424+else:
1425+ from configparser import ConfigParser
1426+ from io import StringIO
1427
1428-for num in sys.argv[1:]:
1429- bug = Bug(num)
1430+UCT_PATH = os.path.expanduser('~/.ubuntu-cve-tracker.conf')
1431
1432- comment = Bug.NewComment(text='Thanks for taking the time to report this bug and helping to make Ubuntu better. We appreciate the difficulties you are facing, but this appears to be a "regular" (non-security) bug. I have unmarked it as a security issue since this bug does not show evidence of allowing attackers to cross privilege boundaries nor directly cause loss of data/privacy. Please feel free to report any other bugs you may find.', subject='')
1433- bug.comments.add(comment)
1434- bug.private = False
1435- bug.security = False
1436
1437- try:
1438- bug.subscribers.add("ubuntu-bugs")
1439- except:
1440- pass
1441+def get_config():
1442+ # replace configobj functionality to avoid extra dependency
1443+ config_content = StringIO()
1444+ config_content.write("[dummy_section]\n")
1445+ with open(UCT_PATH, "r") as fh:
1446+ config_content.write(fh.read())
1447+ config_content.seek(0)
1448+ parser = ConfigParser()
1449+ read_file_func = getattr(parser, "read_file", getattr(parser, "readfp", None))
1450+ if read_file_func is not None:
1451+ read_file_func(config_content)
1452+ return {k: v for k, v in parser.items("dummy_section")}
1453
1454- try:
1455- bug.subscribtions.remove("ubuntu-security")
1456- except:
1457- pass
1458
1459- bug.commit(force_changes=True, ignore_lp_errors=False)
1460+def remove_security(*bugs):
1461+ Bug = Connector.ConnectBug()
1462+ config = get_config()
1463+ Bug.authentication = os.path.join(config['plb_authentication'])
1464+
1465+ for num in bugs:
1466+ bug = Bug(num)
1467+
1468+ comment = Bug.NewComment(text='Thanks for taking the time to report this bug and helping to make Ubuntu better. We appreciate the difficulties you are facing, but this appears to be a "regular" (non-security) bug. I have unmarked it as a security issue since this bug does not show evidence of allowing attackers to cross privilege boundaries nor directly cause loss of data/privacy. Please feel free to report any other bugs you may find.', subject='')
1469+ bug.comments.add(comment)
1470+ bug.private = False
1471+ bug.security = False
1472+
1473+ try:
1474+ bug.subscribers.add("ubuntu-bugs")
1475+ except:
1476+ pass
1477+
1478+ try:
1479+ bug.subscribtions.remove("ubuntu-security")
1480+ except:
1481+ pass
1482+
1483+ bug.commit(force_changes=True, ignore_lp_errors=False)
1484+
1485+
1486+if __name__ == "__main__":
1487+ remove_security(*sys.argv[1:])
1488\ No newline at end of file
1489diff --git a/p-l-p/reassign.py b/p-l-p/reassign.py
1490index d2d18f3..6690cec 100755
1491--- a/p-l-p/reassign.py
1492+++ b/p-l-p/reassign.py
1493@@ -1,7 +1,8 @@
1494-#!/usr/bin/env python
1495+#!/usr/bin/env python3
1496 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1497 # License: GPLv2
1498 # Change the cookie path below...
1499+from __future__ import absolute_import, print_function
1500 import sys
1501 import os, os
1502 import launchpadbugs.connector as Connector
1503@@ -22,26 +23,27 @@ if assignee == 'None':
1504 assignee = None
1505
1506 for num in sys.argv[5:]:
1507- bug = Bug(num)
1508+ bug = Bug(num)
1509
1510- for info in bug.get_infotable():
1511- if info.target and info.target.lower() != 'ubuntu':
1512- print 'skipping target "%s"' % (info.target)
1513- continue
1514- if info.targeted_to == 'Intrepid' or info.targeted_to == None or info.status == 'Fix Released' or info.status == 'Incomplete' or info.status == 'Invalid':
1515- print 'skipping (targeted_to:%s status:%s)' % (info.targeted_to,info.status)
1516- continue
1517- if info.affects == pkg:
1518- if status != "-" and info.status != status:
1519- print '%s: status => %s' % (info.targeted_to,status)
1520- info.status = status
1521- if importance != "-" and info.importance != importance:
1522- print '%s: status => %s' % (info.targeted_to,importance)
1523- info.importance = importance
1524- if assignee != "-" and info.assignee != assignee:
1525- print '%s: assignee => %s' % (info.targeted_to,assignee)
1526- info.assignee = assignee
1527- else:
1528- print 'skipping affects "%s"' % (info.affects)
1529+ for info in bug.get_infotable():
1530+ if info.target and info.target.lower() != 'ubuntu':
1531+ print('skipping target "%s"' % (info.target))
1532+ continue
1533+ if info.targeted_to == 'Intrepid' or info.targeted_to == None or info.status == 'Fix Released' or info.status == 'Incomplete' or info.status == 'Invalid':
1534+ print('skipping (targeted_to:%s status:%s)' % (info.targeted_to,info.status))
1535+ continue
1536+ if info.affects == pkg:
1537+ if status != "-" and info.status != status:
1538+ print('%s: status => %s' % (info.targeted_to,status))
1539+ info.status = status
1540+ if importance != "-" and info.importance != importance:
1541+ print('%s: status => %s' % (info.targeted_to,importance))
1542+ info.importance = importance
1543+ if assignee != "-" and info.assignee != assignee:
1544+ print('%s: assignee => %s' % (info.targeted_to,assignee))
1545+ info.assignee = assignee
1546+ else:
1547+ print('skipping affects "%s"' % (info.affects))
1548+
1549+ bug.commit(force_changes=True, ignore_lp_errors=False)
1550
1551- bug.commit(force_changes=True, ignore_lp_errors=False)
1552diff --git a/p-l-p/requires-trojaned-account.py b/p-l-p/requires-trojaned-account.py
1553index 3619b04..0970eee 100755
1554--- a/p-l-p/requires-trojaned-account.py
1555+++ b/p-l-p/requires-trojaned-account.py
1556@@ -1,7 +1,8 @@
1557-#!/usr/bin/env python
1558+#!/usr/bin/env python3
1559 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1560 # License: GPLv2
1561 # Change the cookie path below...
1562+from __future__ import absolute_import, print_function
1563 import sys, os
1564 import launchpadbugs.connector as Connector
1565
1566diff --git a/p-l-p/set-package.py b/p-l-p/set-package.py
1567index def815c..1a62e14 100755
1568--- a/p-l-p/set-package.py
1569+++ b/p-l-p/set-package.py
1570@@ -1,4 +1,5 @@
1571-#!/usr/bin/env python
1572+#!/usr/bin/env python3
1573+from __future__ import absolute_import, print_function
1574 import sys, os
1575 import launchpadbugs.connector as Connector
1576
1577@@ -6,7 +7,7 @@ Bug = Connector.ConnectBug()
1578 Bug.authentication = os.path.expanduser('~/.plb_cookies')
1579
1580 if len(sys.argv) < 2:
1581- print 'Usage: set-package.py PKG bug [bug ...]'
1582+ print('Usage: set-package.py PKG bug [bug ...]')
1583 sys.exit(1)
1584
1585 pkg = sys.argv[1]
1586diff --git a/p-l-p/sub-security.py b/p-l-p/sub-security.py
1587index decca39..5259438 100755
1588--- a/p-l-p/sub-security.py
1589+++ b/p-l-p/sub-security.py
1590@@ -1,26 +1,33 @@
1591-#!/usr/bin/env python
1592+#!/usr/bin/env python3
1593 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1594 # License: GPLv2
1595 # Change the cookie path below...
1596 # Change 'keescook' to self below... (3 times)
1597+from __future__ import absolute_import, print_function
1598 import sys, os
1599 import launchpadbugs.connector as Connector
1600
1601 Bug = Connector.ConnectBug()
1602 Bug.authentication = os.path.expanduser('~/.plb_cookies')
1603
1604-for num in sys.argv[1:]:
1605- print '%s ... ' % (num)
1606- try:
1607- bug = Bug(num)
1608- except:
1609- print 'private'
1610- continue
1611
1612- if not bug.security:
1613- continue
1614+def subscribe_ubuntu_security(*bugs)
1615+ for num in bugs:
1616+ print('%s ... ' % (num))
1617+ try:
1618+ bug = Bug(num)
1619+ except:
1620+ print('private')
1621+ continue
1622
1623- if not 'ubuntu-security' in bug.subscriptions['directly']:
1624- bug.subscribers.add("ubuntu-security")
1625+ if not bug.security:
1626+ continue
1627
1628- bug.commit(force_changes=True, ignore_lp_errors=False)
1629+ if not 'ubuntu-security' in bug.subscriptions['directly']:
1630+ bug.subscribers.add("ubuntu-security")
1631+
1632+ bug.commit(force_changes=True, ignore_lp_errors=False)
1633+
1634+
1635+if __name__ == "__main__":
1636+ subscribe_ubuntu_security(*sys.argv[1:])
1637\ No newline at end of file
1638diff --git a/p-l-p/tag.py b/p-l-p/tag.py
1639index 466e46b..2a59136 100755
1640--- a/p-l-p/tag.py
1641+++ b/p-l-p/tag.py
1642@@ -2,6 +2,7 @@
1643 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1644 # License: GPLv2
1645 # Change the cookie path below...
1646+from __future__ import absolute_import, print_function
1647 import sys, os
1648 import launchpadbugs.connector as Connector
1649
1650@@ -9,14 +10,14 @@ Bug = Connector.ConnectBug()
1651 Bug.authentication = os.path.expanduser('~/.plb_cookies')
1652
1653 if len(sys.argv) < 3:
1654- print 'Usage: tag.py [+|-]TAG bug [bug ...]'
1655+ print('Usage: tag.py [+|-]TAG bug [bug ...]')
1656 sys.exit(1)
1657
1658 tag = sys.argv[1]
1659 state = tag[0]
1660 tag = tag[1:]
1661 if state not in ['-','+']:
1662- print 'Tag must start with - or +'
1663+ print('Tag must start with - or +')
1664 sys.exit(1)
1665
1666 for num in sys.argv[2:]:
1667diff --git a/p-l-p/unsub-in-dups.py b/p-l-p/unsub-in-dups.py
1668index 24a2d97..cc6eb4c 100755
1669--- a/p-l-p/unsub-in-dups.py
1670+++ b/p-l-p/unsub-in-dups.py
1671@@ -1,8 +1,9 @@
1672-#!/usr/bin/env python
1673+#!/usr/bin/env python3
1674 # Copyright (C) 2007 Canonical, Ltd. Kees Cook <kees@ubuntu.com>
1675 # License: GPLv2
1676 # Change the cookie path below...
1677 # Change 'keescook' below... (twice)
1678+from __future__ import absolute_import, print_function
1679 import sys, os
1680 import launchpadbugs.connector as Connector
1681
1682@@ -12,7 +13,7 @@ Bug = Connector.ConnectBug()
1683 Bug.authentication = os.path.expanduser('~/.plb_cookies')
1684
1685 def not_security(num):
1686- print '%s ...' % (num),
1687+ print('%s ...' % (num), end=' ')
1688 bug = Bug(num)
1689
1690 changed = False
1691@@ -22,9 +23,9 @@ def not_security(num):
1692
1693 if changed:
1694 bug.commit(force_changes=True, ignore_lp_errors=False)
1695- print 'updated'
1696+ print('updated')
1697 else:
1698- print 'skipped'
1699+ print('skipped')
1700 return bug
1701
1702 for num in sys.argv[2:]:
1703diff --git a/package-tools/debcompare b/package-tools/debcompare
1704index 5a3eac7..21e5538 100755
1705--- a/package-tools/debcompare
1706+++ b/package-tools/debcompare
1707@@ -1,4 +1,4 @@
1708-#!/usr/bin/python3
1709+#!/usr/bin/env python3
1710
1711 # Name: debcompare
1712 # Description: Compares different versions of binary packages
1713@@ -16,7 +16,7 @@
1714 #
1715 # License: GPLv3
1716 #
1717-from __future__ import print_function
1718+from __future__ import absolute_import, print_function
1719
1720 import difflib
1721 import os
1722@@ -82,10 +82,9 @@ class DebFile:
1723 '''Gets the symbols of the file specified'''
1724
1725 symbols = []
1726- report = subprocess.check_output(["nm", "-DC", filename])
1727+ report = subprocess.check_output(["nm", "-DC", filename], universal_newlines=True)
1728
1729 for line in report.splitlines():
1730- line = line.decode()
1731 if line[0] == ' ':
1732 # no address prefix, just take everything
1733 symbol_entry = line.strip().split(maxsplit=2)[0:]
1734@@ -652,29 +651,35 @@ def compare_debs(old_deb, new_deb):
1735 new_deb.jarfiles, new_deb.files_dir)
1736
1737
1738-if len(sys.argv) < 3:
1739- print_usage()
1740- sys.exit(1)
1741+if __name__ == "__main__":
1742+ # * Code that is unguarded by this check will run if the file is ever
1743+ # * used in an import statement, even if we only plan to use or import
1744+ # * one function from this file. Guarding code behind this check means
1745+ # * it only runs when the file is invoked directly (i.e. ./myfile.py)
1746+ # * or via the interpreter, i.e. python myfile.py
1747+ if len(sys.argv) < 3:
1748+ print_usage()
1749+ sys.exit(1)
1750
1751-oldfile = os.path.abspath(sys.argv[1])
1752-newfile = os.path.abspath(sys.argv[2])
1753+ oldfile = os.path.abspath(sys.argv[1])
1754+ newfile = os.path.abspath(sys.argv[2])
1755
1756-if not os.path.isfile(oldfile) or not os.path.isfile(newfile):
1757- print_usage()
1758- sys.exit(1)
1759+ if not os.path.isfile(oldfile) or not os.path.isfile(newfile):
1760+ print_usage()
1761+ sys.exit(1)
1762
1763-check_required_tools()
1764+ check_required_tools()
1765
1766-old_deb = DebFile(oldfile)
1767-new_deb = DebFile(newfile)
1768+ old_deb = DebFile(oldfile)
1769+ new_deb = DebFile(newfile)
1770
1771-compare_debs(old_deb, new_deb)
1772+ compare_debs(old_deb, new_deb)
1773
1774-# Clean up temp dirs
1775-if old_deb.temp_dir and os.path.exists(old_deb.temp_dir):
1776- shutil.rmtree(old_deb.temp_dir, ignore_errors=True)
1777-if new_deb.temp_dir and os.path.exists(new_deb.temp_dir):
1778- shutil.rmtree(new_deb.temp_dir, ignore_errors=True)
1779+ # Clean up temp dirs
1780+ if old_deb.temp_dir and os.path.exists(old_deb.temp_dir):
1781+ shutil.rmtree(old_deb.temp_dir, ignore_errors=True)
1782+ if new_deb.temp_dir and os.path.exists(new_deb.temp_dir):
1783+ shutil.rmtree(new_deb.temp_dir, ignore_errors=True)
1784
1785-print("\n\n--------------")
1786-print("End of report.")
1787+ print("\n\n--------------")
1788+ print("End of report.")
1789diff --git a/repo-tools/count-hardened-pkgs b/repo-tools/count-hardened-pkgs
1790index 60d860a..f5b75cf 100755
1791--- a/repo-tools/count-hardened-pkgs
1792+++ b/repo-tools/count-hardened-pkgs
1793@@ -1,9 +1,13 @@
1794-#!/usr/bin/python
1795+#!/usr/bin/env python3
1796 # 2012, Kees Cook <kees@debian.org>
1797
1798+from __future__ import absolute_import, print_function
1799 import time, tempfile, shutil, sys, os, stat, glob
1800-import cPickle as pickle
1801-from subprocess import *
1802+import subprocess
1803+if sys.version_info[0] == 2:
1804+ import cPickle as pickle
1805+else:
1806+ import pickle
1807
1808 count_names = ['elf', 'fortify', 'stackprotector', 'relro', 'bindnow', 'pie']
1809 run_time = time.strftime("%Y%m%d")
1810@@ -23,26 +27,26 @@ class Archive():
1811 else:
1812 components = [component]
1813
1814- print "Loading %s ..." % (release)
1815+ print("Loading %s ..." % (release))
1816 for component in components:
1817- print "\t%s ..." % (component)
1818+ print("\t%s ..." % (component))
1819 packages = os.path.join(base, 'dists', release, component,
1820 'binary-%s' % (arch), 'Packages')
1821 packages_xz = packages + ".xz"
1822 packages_bz2 = packages + ".bz2"
1823 packages_gz = packages + ".gz"
1824 if os.path.exists(packages):
1825- cat = Popen(["cat", packages], stdout=PIPE)
1826+ cat = subprocess.Popen(["cat", packages], stdout=subprocess.PIPE)
1827 elif os.path.exists(packages_xz):
1828- cat = Popen(["xzcat", packages_xz], stdout=PIPE)
1829+ cat = subprocess.Popen(["xzcat", packages_xz], stdout=subprocess.PIPE)
1830 elif os.path.exists(packages_bz2):
1831- cat = Popen(["bzcat", packages_bz2], stdout=PIPE)
1832+ cat = subprocess.Popen(["bzcat", packages_bz2], stdout=subprocess.PIPE)
1833 elif os.path.exists(packages_gz):
1834- cat = Popen(["zcat", packages_gz], stdout=PIPE)
1835+ cat = subprocess.Popen(["zcat", packages_gz], stdout=subprocess.PIPE)
1836 else:
1837- raise ValueError, "Missing %s*" % (packages)
1838- p = Popen(["grep-dctrl", "-s", "Package,Source,Filename", "."],
1839- stdin=cat.stdout, stdout=PIPE)
1840+ raise ValueError("Missing %s*" % (packages))
1841+ p = subprocess.Popen(["grep-dctrl", "-s", "Package,Source,Filename", "."],
1842+ stdin=cat.stdout, stdout=subprocess.PIPE)
1843
1844 pkg = ""
1845 src = ""
1846@@ -62,7 +66,7 @@ class Archive():
1847 self.srcs[src].setdefault(pkg, line.split(' ')[1])
1848 cat.wait()
1849 if cat.returncode != 0:
1850- raise ValueError, "cat %s: %d" % (packges, cat.returncode)
1851+ raise ValueError("cat %s: %d" % (packges, cat.returncode))
1852
1853 def sources(self):
1854 return sorted(self.srcs)
1855@@ -77,7 +81,7 @@ class HardeningReporter():
1856 def __init__(self, cachedir):
1857 # Load version cache.
1858 self.pickle = os.path.join(cachedir, 'cache.pickle')
1859- print "Loading package cache ..."
1860+ print("Loading package cache ...")
1861 try:
1862 self.seen = pickle.load(open(self.pickle, "rb"))
1863 except:
1864@@ -132,9 +136,15 @@ class HardeningReporter():
1865 tmpdir = tempfile.mkdtemp(prefix='unpack-%s-%s-' % (src, pkg))
1866
1867 # Unpack.
1868- print "\t\tUnpacking into %s ..." % (tmpdir)
1869- if call(['dpkg-deb', '-x', deb, tmpdir]) != 0:
1870- print >> sys.stderr, "Could not unpack '%s'!?" % (deb)
1871+ print("\t\tUnpacking into %s ..." % (tmpdir))
1872+ try:
1873+ # XXX: subprocess.call is less preferable compared with check_call
1874+ # XXX: when we are only interested in handling failure cases and
1875+ # XXX: not specific outputs and return codes -- by default we can
1876+ # XXX: do nothing, this does relevant checks for us
1877+ subprocess.check_call(['dpkg-deb', '-x', deb, tmpdir])
1878+ except subprocess.CalledProcessError:
1879+ print("Could not unpack '%s'!?" % (deb), file=sys.stderr)
1880 # Force this package to be ignored.
1881 seen[src][pkg]['counts']['elf'] = 0
1882 return seen[src][pkg]['counts']
1883@@ -164,7 +174,7 @@ class HardeningReporter():
1884 if len(elfs) > 0:
1885 #print elfs
1886 badpie = 0
1887- p = Popen(['hardening-check'] + elfs, stdout=PIPE)
1888+ p = subprocess.Popen(['hardening-check'] + elfs, stdout=subprocess.PIPE)
1889 for line in p.stdout:
1890 if 'Position Independent Executable: ' in line:
1891 if ': yes' in line:
1892@@ -194,7 +204,7 @@ class HardeningReporter():
1893 shutil.rmtree(tmpdir, ignore_errors=False)
1894 except:
1895 # Fix insane unpacks.
1896- call(['chmod', '-R', 'u+rwx', tmpdir])
1897+ subprocess.check_call(['chmod', '-R', 'u+rwx', tmpdir])
1898 shutil.rmtree(tmpdir, ignore_errors=True)
1899
1900 # Finished scanning this src/pkg combo, store it.
1901@@ -207,7 +217,7 @@ class HardeningReporter():
1902 class CacheArchive():
1903 def __init__(self, srcs, reporter):
1904 self.cache, self.mapping = reporter.cache()
1905- print "Mapping source packages ..."
1906+ print("Mapping source packages ...")
1907 self.bins = [x.strip() for x in open(srcs, "r").readlines()]
1908 self.srcs = dict()
1909 for pkg in self.bins:
1910@@ -225,22 +235,6 @@ class CacheArchive():
1911 return self.cache[src][pkg]['filename']
1912
1913
1914-release = None
1915-arch = None
1916-component = None
1917-try:
1918- output = sys.argv[1]
1919- mirror = sys.argv[2]
1920- if len(sys.argv) > 3:
1921- release = sys.argv[3]
1922- arch = sys.argv[4]
1923- if len(sys.argv) > 5:
1924- component = sys.argv[5]
1925-except:
1926- print 'Usage: %s OUTDIR [BINPKGLIST|MIRROR RELEASE ARCH [COMPONENT]]' % (sys.argv[0])
1927- print 'Example: %s /tmp/info /var/cache/mirrors/debian unstable amd64 main' % (sys.argv[0])
1928- sys.exit(1)
1929-
1930 def scan_archive(reporter, archive):
1931 # The master package feature counts
1932 counts = dict()
1933@@ -252,15 +246,15 @@ def scan_archive(reporter, archive):
1934 src_counts.setdefault('elf', 0)
1935 badpie = 0
1936
1937- print "Source: %s ..." % (src)
1938+ print("Source: %s ..." % (src))
1939 for pkg in archive.binary_packages(src):
1940 deb = archive.package_path(src, pkg)
1941- print "\t%s (%s) ..." % (pkg, deb)
1942+ print("\t%s (%s) ..." % (pkg, deb))
1943 bin_counts = reporter.examine(src, pkg, deb)
1944 if bin_counts['elf'] == 0:
1945 # Ignore this deb.
1946 continue
1947- print "\t\t%s" % (repr(bin_counts))
1948+ print("\t\t%s" % (repr(bin_counts)))
1949
1950 # If we have a binary package with ELF and no PIE,
1951 # this means a non-PIE executable was found.
1952@@ -278,7 +272,7 @@ def scan_archive(reporter, archive):
1953 src_counts.setdefault('pie', 0)
1954 src_counts['pie'] = 0
1955
1956- print "\t%s" % (repr(src_counts))
1957+ print("\t%s" % (repr(src_counts)))
1958
1959 # If at least 1 binary's ELF has the option, count it as in source,
1960 # except for PIE, which is handled above as lacking non-PIE.
1961@@ -286,23 +280,47 @@ def scan_archive(reporter, archive):
1962 for name in count_names:
1963 if src_counts[name] > 0:
1964 counts[name] += 1
1965- print "%s\n" % (repr(counts))
1966+ print("%s\n" % (repr(counts)))
1967
1968 return counts
1969
1970
1971-reporter = HardeningReporter(output)
1972-
1973-if release == None:
1974- archive = CacheArchive(mirror, reporter)
1975-else:
1976- print "Loading archive ..."
1977- archive = Archive(mirror, release, arch, component)
1978-
1979-counts = scan_archive(reporter, archive)
1980-
1981-# Append graph data.
1982-for name in counts:
1983- report = open(os.path.join(output, '%s.data' % (name)), "a")
1984- report.write('%s %d\n' % (run_time, counts[name]))
1985- report.close()
1986+if __name__ == "__main__":
1987+ # * Code that is unguarded by this check will run if the file is ever
1988+ # * used in an import statement, even if we only plan to use or import
1989+ # * one function from this file. Guarding code behind this check means
1990+ # * it only runs when the file is invoked directly (i.e. ./myfile.py)
1991+ # * or via the interpreter, i.e. python myfile.py
1992+ release = None
1993+ arch = None
1994+ component = None
1995+ # TODO: switch to argparse
1996+ try:
1997+ output = sys.argv[1]
1998+ mirror = sys.argv[2]
1999+ if len(sys.argv) > 3:
2000+ release = sys.argv[3]
2001+ arch = sys.argv[4]
2002+ if len(sys.argv) > 5:
2003+ component = sys.argv[5]
2004+ except:
2005+ print('Usage: %s OUTDIR [BINPKGLIST|MIRROR RELEASE ARCH [COMPONENT]]' % (sys.argv[0]))
2006+ print('Example: %s /tmp/info /var/cache/mirrors/debian unstable amd64 main' % (sys.argv[0]))
2007+ sys.exit(1)
2008+
2009+ reporter = HardeningReporter(output)
2010+
2011+ if release == None:
2012+ archive = CacheArchive(mirror, reporter)
2013+ else:
2014+ print("Loading archive ...")
2015+ archive = Archive(mirror, release, arch, component)
2016+
2017+ counts = scan_archive(reporter, archive)
2018+
2019+ # Append graph data.
2020+ for name in counts:
2021+ # * Open files in context managers to make sure file handles are
2022+ # * closed properly before the process is cleaned up
2023+ with open(os.path.join(output, '%s.data' % (name)), "a") as report:
2024+ report.write('%s %d\n' % (run_time, counts[name]))
2025diff --git a/repo-tools/count-hash-sources b/repo-tools/count-hash-sources
2026index 48e6c2d..de5fbf4 100755
2027--- a/repo-tools/count-hash-sources
2028+++ b/repo-tools/count-hash-sources
2029@@ -1,12 +1,20 @@
2030-#!/usr/bin/python
2031+#!/usr/bin/env python3
2032
2033+from __future__ import absolute_import, print_function
2034 import deb822 # from python-debian
2035-import gzip
2036 import argparse
2037+import contextlib
2038+import gzip
2039+import io
2040 import os
2041 import re
2042 import sys
2043-import urllib2
2044+
2045+if sys.version_info[0] == 2:
2046+ from urllib2 import urlopen
2047+else:
2048+ from urllib.request import urlopen
2049+
2050
2051 components = ['main', 'multiverse', 'restricted', 'universe']
2052 default_releases = ['precise', 'trusty', 'xenial', 'bionic', 'cosmic', 'disco']
2053@@ -16,22 +24,23 @@ def count_md5only_sources(sources_list):
2054 md5only = 0
2055 sha1only = 0
2056 langpack = 0
2057- for src in deb822.Sources.iter_paragraphs(file(sources_list)):
2058- count += 1
2059- if not src.has_key('Checksums-Sha256'):
2060- sha1only += 1
2061- if not src.has_key('Checksums-Sha1'):
2062- if re.match("language-pack-.*", src['Package']):
2063- langpack += 1
2064- else:
2065- md5only += 1
2066- #print src['Package']
2067- if not src.has_key('Files'):
2068- print "Bizarre, src package '%s' has no Files section" %(src['Package'])
2069+ with open(sources_list, "r") as sources_handle:
2070+ for src in deb822.Sources.iter_paragraphs(sources_handle):
2071+ count += 1
2072+ if 'Checksums-Sha256' not in src:
2073+ sha1only += 1
2074+ if 'Checksums-Sha1' not in src:
2075+ if re.match("language-pack-.*", src['Package']):
2076+ langpack += 1
2077+ else:
2078+ md5only += 1
2079+ #print src['Package']
2080+ if 'Files' not in src:
2081+ print("Bizarre, src package '%s' has no Files section" %(src['Package']))
2082 return (count, md5only, sha1only, langpack)
2083
2084 def download_release_files(release):
2085- print "Downloading release files for %s" % release
2086+ print("Downloading release files for %s" % release)
2087
2088 for component in components:
2089
2090@@ -42,39 +51,43 @@ def download_release_files(release):
2091 continue
2092
2093 url= "http://archive.ubuntu.com/ubuntu/dists/%s/%s/source/Sources.gz" % (release, component)
2094- print "Downloading %s..." % url
2095-
2096- # Download it
2097- open_url = urllib2.urlopen(url)
2098- output = open('Sources.gz','wb')
2099- output.write(open_url.read())
2100- output.close()
2101-
2102- # Now uncompress it
2103- f_in = gzip.open('Sources.gz', 'rb')
2104- f_out = open(file_name, 'wb')
2105- f_out.writelines(f_in)
2106- f_out.close()
2107- f_in.close()
2108-
2109- os.unlink('Sources.gz')
2110-
2111- print "Download completed."
2112-
2113-parser = argparse.ArgumentParser(description='Finds source packages with md5 only hash sums')
2114-parser.add_argument('-r', '--release', action='append', help="find sources in a specific release")
2115-args = parser.parse_args()
2116-
2117-if args.release:
2118- releases = args.release
2119-else:
2120- releases = default_releases
2121-
2122-for release in releases:
2123- download_release_files(release)
2124-
2125-for release in releases:
2126- for component in components:
2127- (total, md5only, sha1only, langpack) = count_md5only_sources("%s-sources-%s" %(release, component))
2128- print "%s %s: \ttotal: %d \tmd5only: %d\tsha1only: %d\tlangpacks: %d" %(release, component, total, md5only, sha1only, langpack)
2129- print
2130+ print("Downloading %s..." % url)
2131+
2132+ # * Download compressed contents and write it to a buffer
2133+ compressed_contents = io.BytesIO()
2134+ with contextlib.closing(urlopen(url)) as response:
2135+ compressed_contents.write(response.read())
2136+ compressed_contents.seek(0)
2137+
2138+ # * Now lets unzip the buffer, decode it, clean up line endings and
2139+ # * write to a target file
2140+ with gzip.GzipFile(fileobj=compressed_contents, mode="rb") as f_in:
2141+ with open(file_name, "w") as f_out:
2142+ f_out.write(f_in.read().decode("utf-8").replace(r"\r\n", r"\n"))
2143+
2144+ print("Download completed.")
2145+
2146+
2147+if __name__ == "__main__":
2148+ # * Code that is unguarded by this check will run if the file is ever
2149+ # * used in an import statement, even if we only plan to use or import
2150+ # * one function from this file. Guarding code behind this check means
2151+ # * it only runs when the file is invoked directly (i.e. ./myfile.py)
2152+ # * or via the interpreter, i.e. python myfile.py
2153+ parser = argparse.ArgumentParser(description='Finds source packages with md5 only hash sums')
2154+ parser.add_argument('-r', '--release', action='append', help="find sources in a specific release")
2155+ args = parser.parse_args()
2156+
2157+ if args.release:
2158+ releases = args.release
2159+ else:
2160+ releases = default_releases
2161+
2162+ for release in releases:
2163+ download_release_files(release)
2164+
2165+ for release in releases:
2166+ for component in components:
2167+ total, md5only, sha1only, langpack = count_md5only_sources("%s-sources-%s" %(release, component))
2168+ print("%s %s: \ttotal: %d \tmd5only: %d\tsha1only: %d\tlangpacks: %d" %(release, component, total, md5only, sha1only, langpack))
2169+ print()
2170diff --git a/repo-tools/for-archive-tools/has-execstack b/repo-tools/for-archive-tools/has-execstack
2171index 4d53338..8453f2b 100755
2172--- a/repo-tools/for-archive-tools/has-execstack
2173+++ b/repo-tools/for-archive-tools/has-execstack
2174@@ -1,27 +1,42 @@
2175-#!/usr/bin/python
2176+#!/usr/bin/env python3
2177 # Copyright 2009 Canonical, Ltd
2178 # Author: Kees Cook <kees@ubuntu.com>
2179 # License: GPLv3
2180 #
2181 # Expects to be run via "unpack-search" within "for-archive"
2182 # Checks only ELF files for execstack, and reports anything non-exec or unknown
2183-import sys, subprocess
2184+from __future__ import absolute_import, print_function
2185+import os
2186+import subprocess
2187+import sys
2188 files = sys.argv[1:]
2189 if files[0] == '--':
2190 files.pop(0)
2191 if files[0] == '--':
2192 files.pop(0)
2193-devnull = open('/dev/null','w')
2194-for f in files:
2195- #print >>sys.stderr, f
2196- try:
2197- ok = (open(f).read(4) == '\x7fELF')
2198- except:
2199- continue
2200- if ok:
2201- out = subprocess.Popen(['execstack','-q',f],stdout=subprocess.PIPE,stderr=devnull).communicate()[0]
2202- for line in out.splitlines():
2203- line = line.strip()
2204- if line.startswith('- '):
2205+# * Here we use a context manager again to make sure the handle is closed
2206+with open('/dev/null','w') as devnull:
2207+ for f in files:
2208+ if not os.path.exists(f) and os.path.isfile(f):
2209+ continue
2210+ with open(f, "rb") as fh:
2211+ try:
2212+ ok = f.read(4) == b"\x7fELF"
2213+ except Exception:
2214 continue
2215- print line
2216+ if ok:
2217+ # * the .communicate() call returns a 2-tuple (stdout, stderr), so
2218+ # * we are implicitly throwing away stderr by assigning it to _
2219+ # * which is ignored in python
2220+ # * Note that we are using 'universal_newlines=True' which actually
2221+ # * tells the interpreter to give back unicode output rather than
2222+ # * bytes, so we will be equipped for moving back and forth between
2223+ # * python 2 and python 3
2224+ out, _ = subprocess.Popen(
2225+ ['execstack','-q',f], stdout=subprocess.PIPE, stderr=devnull,
2226+ universal_newlines=True
2227+ ).communicate()
2228+ for line in out.splitlines():
2229+ line = line.strip()
2230+ if not line.startswith('- '):
2231+ print(line)
2232diff --git a/repo-tools/for-archive-tools/has-symbols b/repo-tools/for-archive-tools/has-symbols
2233index a48402c..a62af89 100755
2234--- a/repo-tools/for-archive-tools/has-symbols
2235+++ b/repo-tools/for-archive-tools/has-symbols
2236@@ -1,12 +1,15 @@
2237-#!/usr/bin/python
2238+#!/usr/bin/env python3
2239 # Copyright 2009 Canonical, Ltd
2240 # Author: Kees Cook <kees@ubuntu.com>
2241 # License: GPLv3
2242 #
2243 # Expects to be run via "unpack-search" within "for-archive"
2244 # Checks only ELF files for symbols, and reports anything referencing these symbols
2245-import sys, subprocess
2246+from __future__ import absolute_import, print_function
2247+import os
2248 import re
2249+import subprocess
2250+import sys
2251
2252 symrx = re.compile(r'\*UND\*.*(_ZNSt6chrono12system_clock3nowEv|_ZNSt6chrono12steady_clock3nowEv)')
2253 #symrx = re.compile(r'\*UND\*.*(__fprintf_chk|__progname_full)')
2254@@ -16,17 +19,29 @@ if files[0] == '--':
2255 files.pop(0)
2256 if files[0] == '--':
2257 files.pop(0)
2258-devnull = open('/dev/null','w')
2259-for f in files:
2260- #print >>sys.stderr, f
2261- try:
2262- ok = (open(f).read(4) == '\x7fELF')
2263- except:
2264- continue
2265- if ok:
2266- out = subprocess.Popen(['objdump','-T',f],stdout=subprocess.PIPE,stderr=devnull).communicate()[0]
2267- for line in out.splitlines():
2268- line = line.strip()
2269- if not symrx.search(line):
2270+# * Here we use a context manager again to make sure the handle is closed
2271+with open('/dev/null','w') as devnull:
2272+ for f in files:
2273+ if not os.path.exists(f) and os.path.isfile(f):
2274+ continue
2275+ with open(f, "rb") as fh:
2276+ try:
2277+ ok = f.read(4) == b"\x7fELF"
2278+ except Exception:
2279 continue
2280- print line
2281+ if ok:
2282+ # * the .communicate() call returns a 2-tuple (stdout, stderr), so
2283+ # * we are implicitly throwing away stderr by assigning it to _
2284+ # * which is ignored in python
2285+ # * Note that we are using 'universal_newlines=True' which actually
2286+ # * tells the interpreter to give back unicode output rather than
2287+ # * bytes, so we will be equipped for moving back and forth between
2288+ # * python 2 and python 3
2289+ out, _ = subprocess.Popen(
2290+ ['objdump', '-T' ,f], stdout=subprocess.PIPE, stderr=devnull,
2291+ universal_newlines=True
2292+ ).communicate()
2293+ for line in out.splitlines():
2294+ line = line.strip()
2295+ if symrx.search(line):
2296+ print(line)
2297\ No newline at end of file
2298diff --git a/repo-tools/packages-support-status b/repo-tools/packages-support-status
2299index 9817a80..c0082f6 100755
2300--- a/repo-tools/packages-support-status
2301+++ b/repo-tools/packages-support-status
2302@@ -1,4 +1,4 @@
2303-#!/usr/bin/python3
2304+#!/usr/bin/env python3
2305
2306 # Author: Marc Deslauriers <marc.deslauriers@ubuntu.com>
2307 # Copyright (C) 2012 Canonical Ltd.
2308@@ -12,17 +12,23 @@
2309 #
2310 # You can download the Release files for i386 with the --download command.
2311 #
2312-from __future__ import print_function
2313+from __future__ import absolute_import, print_function
2314
2315+import contextlib
2316+import io
2317 import gzip
2318 import os
2319 import re
2320 import sys
2321-import urllib.error
2322-import urllib.parse
2323-import urllib.request
2324 from optparse import OptionParser
2325
2326+if sys.version_info[0] == 2:
2327+ from urllib2 import urlopen
2328+else:
2329+ from urllib.request import urlopen
2330+
2331+# TODO: Switch to argparse
2332+
2333 components = ['main', 'multiverse', 'restricted', 'universe']
2334
2335 def parse_package_files():
2336@@ -84,56 +90,59 @@ def download_release_files(release):
2337 url= "http://archive.ubuntu.com/ubuntu/dists/%s/%s/binary-i386/Packages.gz" % (release, component)
2338 print("Downloading %s..." % url)
2339
2340- # Download it
2341- open_url = urllib.request.urlopen(url)
2342- output = open('Packages.gz', 'wb')
2343- output.write(open_url.read())
2344- output.close()
2345-
2346- # Now uncompress it
2347- f_in = gzip.open('Packages.gz', 'rb')
2348- f_out = open(file_name, 'wb')
2349- f_out.writelines(f_in)
2350- f_out.close()
2351- f_in.close()
2352-
2353- os.unlink('Packages.gz')
2354-
2355+ # * Download compressed contents and write it to a buffer
2356+ compressed_contents = io.BytesIO()
2357+ with contextlib.closing(urlopen(url)) as response:
2358+ compressed_contents.write(response.read())
2359+ compressed_contents.seek(0)
2360+
2361+ # * Now lets unzip the buffer, decode it, clean up line endings and
2362+ # * write to a target file
2363+ with gzip.GzipFile(fileobj=compressed_contents, mode="rb") as f_in:
2364+ with open(file_name, "w") as f_out:
2365+ f_out.write(f_in.read().decode("utf-8").replace(r"\r\n", r"\n"))
2366 print("Download completed.")
2367
2368 def print_package(package, component, supported):
2369 print("%-50s %-12s %s" % (package, component, supported))
2370
2371-parser = OptionParser()
2372-parser.add_option("", "--show-unsupported",
2373- action="append_const", dest="filter",
2374- const="Unsupported", help="Show unsupported packages")
2375-parser.add_option("", "--show-18m",
2376- action="append_const", dest="filter",
2377- const="18m", help="Show 18m supported packages")
2378-parser.add_option("", "--show-3y",
2379- action="append_const", dest="filter",
2380- const="3y", help="Show 3y supported packages")
2381-parser.add_option("", "--show-5y",
2382- action="append_const", dest="filter",
2383- const="5y", help="Show 5y supported packages")
2384-parser.add_option("", "--download", metavar="RELEASE",
2385- action="store", dest="download",
2386- help="Download release files for RELEASE in current directory")
2387-
2388-(options, args) = parser.parse_args()
2389-
2390-if options.download:
2391- download_release_files(options.download)
2392- sys.exit(0)
2393-
2394-packages = parse_package_files()
2395-all_packages = sorted(packages.keys())
2396-for package in all_packages:
2397-
2398- if options.filter:
2399- if packages[package]['supported'] not in options.filter:
2400- continue
2401-
2402- print_package(package, packages[package]['component'], packages[package]['supported'])
2403+
2404+if __name__ == "__main__":
2405+ # * Code that is unguarded by this check will run if the file is ever
2406+ # * used in an import statement, even if we only plan to use or import
2407+ # * one function from this file. Guarding code behind this check means
2408+ # * it only runs when the file is invoked directly (i.e. ./myfile.py)
2409+ # * or via the interpreter, i.e. python myfile.py
2410+ parser = OptionParser()
2411+ parser.add_option("", "--show-unsupported",
2412+ action="append_const", dest="filter",
2413+ const="Unsupported", help="Show unsupported packages")
2414+ parser.add_option("", "--show-18m",
2415+ action="append_const", dest="filter",
2416+ const="18m", help="Show 18m supported packages")
2417+ parser.add_option("", "--show-3y",
2418+ action="append_const", dest="filter",
2419+ const="3y", help="Show 3y supported packages")
2420+ parser.add_option("", "--show-5y",
2421+ action="append_const", dest="filter",
2422+ const="5y", help="Show 5y supported packages")
2423+ parser.add_option("", "--download", metavar="RELEASE",
2424+ action="store", dest="download",
2425+ help="Download release files for RELEASE in current directory")
2426+
2427+ (options, args) = parser.parse_args()
2428+
2429+ if options.download:
2430+ download_release_files(options.download)
2431+ sys.exit(0)
2432+
2433+ packages = parse_package_files()
2434+ all_packages = sorted(packages.keys())
2435+ for package in all_packages:
2436+
2437+ if options.filter:
2438+ if packages[package]['supported'] not in options.filter:
2439+ continue
2440+
2441+ print_package(package, packages[package]['component'], packages[package]['supported'])
2442
2443diff --git a/repo-tools/universe-binaries-with-sources-in-main.py b/repo-tools/universe-binaries-with-sources-in-main.py
2444index 8c76793..ccd1a03 100755
2445--- a/repo-tools/universe-binaries-with-sources-in-main.py
2446+++ b/repo-tools/universe-binaries-with-sources-in-main.py
2447@@ -1,10 +1,12 @@
2448-#!/usr/bin/python
2449+#!/usr/bin/env python3
2450
2451+from __future__ import absolute_import, print_function
2452 packages = "./universe/binary-i386/Packages"
2453 sources = "./main/source/Sources"
2454
2455 # Get all the binaries in universe
2456-lines = open(packages).readlines()
2457+with open(packages, "r") as fh:
2458+ lines = fh.readlines()
2459 bins = {}
2460 for line in lines:
2461 line = line.strip()
2462@@ -12,6 +14,8 @@ for line in lines:
2463 bins[line.split()[1]] = True
2464
2465 # then see if one of these binaries matches the Binary line in sources from main
2466+with open(sources, "r") as fh:
2467+ lines = fh.readlines()
2468 lines = open(sources).readlines()
2469 srcs = {}
2470 src = None
2471@@ -23,4 +27,4 @@ for line in lines:
2472 for b in line.partition(' ')[2].split(','):
2473 b = b.strip()
2474 if b in bins:
2475- print "%s (src: %s)" % (b, src)
2476+ print("%s (src: %s)" % (b, src))
2477diff --git a/snaps/coverity-ubuntu-security/files/bin/refresh-auth-key b/snaps/coverity-ubuntu-security/files/bin/refresh-auth-key
2478index 88ac51a..9f6cf3c 100755
2479--- a/snaps/coverity-ubuntu-security/files/bin/refresh-auth-key
2480+++ b/snaps/coverity-ubuntu-security/files/bin/refresh-auth-key
2481@@ -1,5 +1,6 @@
2482-#!/usr/bin/python3
2483+#!/usr/bin/env python3
2484
2485+from __future__ import absolute_import, print_function
2486 import json
2487 import os
2488 import subprocess
2489diff --git a/utilities/build_failures.py b/utilities/build_failures.py
2490index 18a2896..d4df8cc 100755
2491--- a/utilities/build_failures.py
2492+++ b/utilities/build_failures.py
2493@@ -1,4 +1,4 @@
2494-#!/usr/bin/python3
2495+#!/usr/bin/env python3
2496
2497 # This is a start of a script that looks for build failures on the
2498 # yaml version of the excuses page from
2499@@ -8,20 +8,26 @@
2500 # In particular, we're looking for failures due to gcc -pie by default
2501 # on amd64, where a build will succeed on i386 but fail on amd64
2502
2503-from yaml import load, dump
2504+from __future__ import absolute_import, print_function
2505+from yaml import safe_load, safe_dump
2506
2507-with open("update_excuses.yaml") as f:
2508- data = load(f)
2509
2510-source_entries = data['sources']
2511+def check_builds():
2512+ with open("update_excuses.yaml", "r") as f:
2513+ data = safe_load(f)
2514
2515-for s in source_entries:
2516- if 'missing-builds' in s.keys():
2517- arches = s['missing-builds']['on-architectures']
2518- if 'amd64' in arches and not 'i386' in arches:
2519- for excuse in s['excuses']:
2520- if excuse.startswith("missing build on") and '>amd64<' in excuse:
2521- url = excuse.split('"')[1]
2522- print('%s: %s' % (s['source'], url))
2523- #print('%s' %s)
2524+ source_entries = data['sources']
2525
2526+ for s in source_entries:
2527+ if 'missing-builds' in s:
2528+ arches = s['missing-builds']['on-architectures']
2529+ if 'amd64' in arches and 'i386' not in arches:
2530+ for excuse in s['excuses']:
2531+ if excuse.startswith("missing build on") and '>amd64<' in excuse:
2532+ url = excuse.split('"')[1]
2533+ print('%s: %s' % (s['source'], url))
2534+ #print('%s' %s)
2535+
2536+
2537+if __name__ == "__main__":
2538+ check_builds()
2539\ No newline at end of file
2540diff --git a/utilities/ceviche b/utilities/ceviche
2541index 546482b..4f7e3c5 100755
2542--- a/utilities/ceviche
2543+++ b/utilities/ceviche
2544@@ -1,4 +1,4 @@
2545-#!/usr/bin/env python
2546+#!/usr/bin/env python3
2547
2548 # Author: Paulo Flabiano Smorigo <pfsmorigo@canonical.com>
2549 # Copyright (C) 2020 Canonical Ltd.
2550@@ -7,17 +7,26 @@
2551 # Public License, Version 2 or later. See http://www.gnu.org/copyleft/gpl.html
2552 # for details.
2553
2554+from __future__ import absolute_import, print_function
2555+import contextlib
2556 import curses
2557+import glob
2558 import json
2559 import optparse
2560 import os
2561-import pickle
2562 import re
2563 import subprocess
2564 import sys
2565 import time
2566-import urllib2
2567-import glob
2568+
2569+if sys.version_info[0] == 2:
2570+ from urllib2 import urlopen
2571+ import cPickle as pickle
2572+ raw_input = raw_input
2573+else:
2574+ from urllib.request import urlopen
2575+ import pickle
2576+ raw_input = input
2577
2578 # cve_lib may not be in PYTHONPATH so try harder to find it
2579 try:
2580@@ -155,8 +164,11 @@ def main_screen(stdscr, opt, args):
2581 colors.append(["high", 197, -1])
2582 colors.append(["critical", 197, -1])
2583
2584- for i in range(0, len(colors)):
2585- curses.init_pair(i + 1, colors[i][1], colors[i][2])
2586+ for i, color_triplet in enumerate(colors, start=1):
2587+ # * Range operations are slightly different between python 2 and 3
2588+ # * so should be avoided wherever possible
2589+ _, fg_color, bg_color = color_triplet
2590+ curses.init_pair(i, fg_color, bg_color)
2591
2592 downloaded = []
2593 if os.path.isdir(DOWNLOAD_PATH):
2594@@ -519,6 +531,7 @@ def main_screen(stdscr, opt, args):
2595 stdscr.addstr(x, columns_start - 7, "%6d" % item[COL_POP])
2596
2597 y = columns_start
2598+ # check the status on each supported release
2599 for i in range(COL_REL, len(RELEASES) + COL_REL):
2600 release_status = item[i]
2601 if release_status == "needed" \
2602@@ -706,7 +719,7 @@ def run_command(stdscr, command, pause = False):
2603 os.system("bash -c \"%s\"" % command)
2604 if pause:
2605 try:
2606- input("\nPress Enter to continue...")
2607+ raw_input("\nPress Enter to continue...")
2608 except:
2609 pass
2610 curses.reset_prog_mode()
2611@@ -755,7 +768,7 @@ def reload_persistent_data(stdscr, opt):
2612
2613 column = COL_REL
2614 for release in RELEASES:
2615- if release in table[cve][package].keys():
2616+ if release in table[cve][package]
2617 item[column] = table[cve][package][release]
2618 else:
2619 item[column] = "---"
2620@@ -782,13 +795,13 @@ def load_persistent_data(stdscr, opt):
2621 return reload_persistent_data(stdscr, opt)
2622
2623 def get_links(filename):
2624- list = []
2625+ match_list = []
2626 with open(filename) as fh:
2627 for line in fh:
2628 match = re.search("(http|https|ftp)://[\w\-]+(\.[\w\-]+)+\S*", line)
2629 if match:
2630- list.insert(len(list), match.group())
2631- return list
2632+ match_list.append(match.group())
2633+ return match_list
2634
2635 def is_patch(url):
2636 if re.search("(http|https|ftp)://.*\/(commit|hg\/rev)\/.*", url):
2637@@ -798,12 +811,10 @@ def is_patch(url):
2638
2639 def save_patches(filename):
2640 cve = os.path.basename(filename)
2641- links = []
2642- for link in get_links(filename):
2643- if is_patch(link):
2644- links.insert(len(links), link)
2645-
2646- links = list(dict.fromkeys(links)) # Remove duplicates
2647+ # * Use set comprehension to eliminate duplicates - creates a set which
2648+ # * by definition only contains unique elements, duplicates are discarded
2649+ # * then we just convert back to a list
2650+ links = list({link for link in get_links(filename) if is_patch(link)})
2651
2652 try:
2653 message = "Saved:"
2654@@ -811,19 +822,22 @@ def save_patches(filename):
2655 link = links[0]
2656 link += ".patch" if link[-6:] != ".patch" else ""
2657
2658- filedata = urllib2.urlopen(link)
2659- datatowrite = filedata.read()
2660+ with contextlib.closing(urlopen(link)) as response:
2661+ filedata = response.read()
2662+ # FIXME: Don't write full absolute paths directly to the filesystem
2663+ # XXX : Use NamedTemporaryFile / mkstemp to generate names
2664 tmpfile = '%s.patch' % cve
2665 message += " %s" % tmpfile
2666 with open('/tmp/' + tmpfile, 'wb') as f:
2667- f.write(datatowrite)
2668+ f.write(filedata)
2669 elif len(links) > 1:
2670- for i in range(0, len(links)):
2671- filedata = urllib2.urlopen("%s.patch" % links[i])
2672- datatowrite = filedata.read()
2673+ for i, link in enumerate(links):
2674+ url = "%s.patch" % link
2675+ with contextlib.closing(urlopen(url)) as response:
2676+ filedata = response.read()
2677 tmpfile = '%s-%d.patch' % (cve, i + 1)
2678 with open('/tmp/' + tmpfile, 'wb') as f:
2679- f.write(datatowrite)
2680+ f.write(filedata)
2681 message += " %s" % tmpfile
2682 return message
2683 except:
2684@@ -832,7 +846,7 @@ def save_patches(filename):
2685 def git_filter(filter):
2686 returnlist = []
2687 p = subprocess.Popen(['git', '-C', PATH, 'status', '-s', '.'],
2688- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
2689+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
2690 out, err = p.communicate()
2691 for line in out.splitlines():
2692 l = line.split()
2693@@ -964,6 +978,7 @@ def parse_opts():
2694 return (opt, args)
2695
2696 def main():
2697+ # TODO: Migrate to argparse
2698 (opt, args) = parse_opts()
2699 if not os.path.isdir(PATH):
2700 print("CVEs directory not found (%s)!" % PATH)
2701diff --git a/utilities/maildir2mbox.py b/utilities/maildir2mbox.py
2702index ef75c99..9abe155 100755
2703--- a/utilities/maildir2mbox.py
2704+++ b/utilities/maildir2mbox.py
2705@@ -1,4 +1,4 @@
2706-#!/usr/bin/python
2707+#!/usr/bin/env python3
2708 #
2709 # Author: Jamie Strandboge <jamie@ubuntu.com>
2710 # Copyright (C) 2005-2011 Canonical Ltd.
2711@@ -11,6 +11,7 @@
2712 # directories.
2713 #
2714
2715+from __future__ import absolute_import, print_function
2716 import datetime
2717 import email
2718 import email.utils
2719@@ -25,20 +26,20 @@ import tempfile
2720 def debug(s):
2721 '''Print debug message'''
2722 if opt.debug:
2723- print >>sys.stderr, "DEBUG: %s" % (s)
2724+ print("DEBUG: %s" % (s), file=sys.stderr)
2725
2726 def get_date(s):
2727 '''Return a datetime.date object from string'''
2728 pat = re.compile(r'^\d\d\d\d-\d\d-\d\d$')
2729 d = None
2730 if not pat.match(s):
2731- print >>sys.stderr, "date '%s' should be YYYY-MM-DD" % (s)
2732+ print("date '%s' should be YYYY-MM-DD" % (s), file=sys.stderr)
2733 return None
2734 t = s.split('-')
2735 try:
2736 d = datetime.date(int(t[0]), int(t[1]), int(t[2]))
2737 except:
2738- print >>sys.stderr, "Invalid date '%s'" % (s)
2739+ print("Invalid date '%s'" % (s), file=sys.stderr)
2740 raise
2741
2742 return d
2743@@ -64,16 +65,16 @@ parser.add_option("--end-date",
2744 (opt, args) = parser.parse_args()
2745
2746 if not opt.folder:
2747- print >>sys.stderr, "Must specify --folder"
2748+ print("Must specify --folder", file=sys.stderr)
2749 sys.exit(1)
2750 elif not opt.mbox:
2751- print >>sys.stderr, "Must specify --mbox"
2752+ print("Must specify --mbox", file=sys.stderr)
2753 sys.exit(1)
2754 elif not os.path.isdir(opt.folder):
2755- print >>sys.stderr, "'%s' does not exist" % opt.folder
2756+ print("'%s' does not exist" % opt.folder, file=sys.stderr)
2757 sys.exit(1)
2758 elif os.path.exists(opt.mbox):
2759- print >>sys.stderr, "'%s' already exists" % opt.mbox
2760+ print("'%s' already exists" % opt.mbox, file=sys.stderr)
2761 sys.exit(1)
2762
2763 start = None
2764@@ -114,7 +115,7 @@ for message in src_maildir:
2765 debug("Skipping: 'Subject: %s' (%s is after %s)" % (message['subject'], msg_date, start))
2766 continue
2767
2768- if message.has_key('subject') and message.has_key('date'):
2769+ if 'subject' in message and 'date' in message:
2770 debug("Processing '%s: Subject: %s'" % (message['date'], message['subject']))
2771
2772 try:
2773diff --git a/utilities/mugshot b/utilities/mugshot
2774index 9f063ea..55dc6ff 100755
2775--- a/utilities/mugshot
2776+++ b/utilities/mugshot
2777@@ -1,9 +1,9 @@
2778-#!/usr/bin/python
2779+#!/usr/bin/env python3
2780 # Author: Kees Cook <kees@ubuntu.com>
2781 # Copyright (C) 2010 Canonical, Ltd.
2782 # License: GPLv3
2783
2784-from __future__ import print_function
2785+from __future__ import absolute_import, print_function
2786 import sys
2787 import optparse
2788 import httplib2

Subscribers

People subscribed via source and target branches