1- from datetime import date
21from datetime import datetime
32from html import unescape
3+ from os .path import join
44from re import IGNORECASE
55from re import escape
66from re import search
77from re import sub
8+ from urllib .parse import urlparse
89
10+ from flask import render_template
911from markupsafe import escape as html_escape
1012from requests import get
1113
14+ from config import TRACKER_ADVISORY_URL
15+ from config import TRACKER_BUGTRACKER_URL
16+ from config import TRACKER_GROUP_URL
17+ from config import TRACKER_ISSUE_URL
1218from config import TRACKER_MAILMAN_URL
19+ from tracker import db
20+ from tracker .model import CVE
21+ from tracker .model import Advisory
22+ from tracker .model import CVEGroup
23+ from tracker .model import CVEGroupEntry
24+ from tracker .model import CVEGroupPackage
25+ from tracker .model import Package
26+ from tracker .model .enum import Publication
27+ from tracker .model .enum import Remote
28+ from tracker .user import user_can_handle_advisory
1329from tracker .util import chunks
1430from tracker .util import issue_to_numeric
31+ from tracker .util import multiline_to_list
32+
33+
34+ def generate_advisory (advisory_id , with_subject = True , raw = True ):
35+ entries = (db .session .query (Advisory , CVEGroup , CVEGroupPackage , CVE )
36+ .filter (Advisory .id == advisory_id )
37+ .join (CVEGroupPackage , Advisory .group_package )
38+ .join (CVEGroup , CVEGroupPackage .group )
39+ .join (CVEGroupEntry , CVEGroup .issues )
40+ .join (CVE , CVEGroupEntry .cve )
41+ .order_by (CVE .id )
42+ ).all ()
43+ if not entries :
44+ return None
45+
46+ advisory = entries [0 ][0 ]
47+ group = entries [0 ][1 ]
48+ package = entries [0 ][2 ]
49+ issues = sorted ([issue for (advisory , group , package , issue ) in entries ])
50+ severity_sorted_issues = sorted (issues , key = lambda issue : issue .issue_type )
51+ severity_sorted_issues = sorted (severity_sorted_issues , key = lambda issue : issue .severity )
52+ remote = any ([issue .remote is Remote .remote for issue in issues ])
53+ issue_listing_formatted = advisory_format_issue_listing ([issue .id for issue in issues ])
54+
55+ link = TRACKER_ADVISORY_URL .format (advisory .id , group .id )
56+ upstream_released = group .affected .split ('-' )[0 ].split ('+' )[0 ] != group .fixed .split ('-' )[0 ].split ('+' )[0 ]
57+ upstream_version = group .fixed .split ('-' )[0 ].split ('+' )[0 ]
58+ if ':' in upstream_version :
59+ upstream_version = upstream_version [upstream_version .index (':' ) + 1 :]
60+ unique_issue_types = []
61+ for issue in severity_sorted_issues :
62+ if issue .issue_type not in unique_issue_types :
63+ unique_issue_types .append (issue .issue_type )
64+
65+ references = []
66+ if group .bug_ticket :
67+ references .append (TRACKER_BUGTRACKER_URL .format (group .bug_ticket ))
68+ references .extend ([ref for ref in multiline_to_list (group .reference )
69+ if ref not in references ])
70+ list (map (lambda issue : references .extend (
71+ [ref for ref in multiline_to_list (issue .reference ) if ref not in references ]), issues ))
72+
73+ raw_asa = render_template ('advisory.txt' ,
74+ advisory = advisory ,
75+ group = group ,
76+ package = package ,
77+ issues = issues ,
78+ remote = remote ,
79+ issue_listing_formatted = issue_listing_formatted ,
80+ link = link ,
81+ workaround = advisory .workaround ,
82+ impact = advisory .impact ,
83+ upstream_released = upstream_released ,
84+ upstream_version = upstream_version ,
85+ unique_issue_types = unique_issue_types ,
86+ references = references ,
87+ with_subject = with_subject ,
88+ TRACKER_ISSUE_URL = TRACKER_ISSUE_URL ,
89+ TRACKER_GROUP_URL = TRACKER_GROUP_URL )
90+ if raw :
91+ return raw_asa
92+
93+ raw_asa = '\n ' .join (raw_asa .split ('\n ' )[2 :])
94+ raw_asa = str (html_escape (raw_asa ))
95+ raw_asa = advisory_extend_html (raw_asa , issues , package )
96+ return render_html_advisory (advisory = advisory , package = package , group = group , raw_asa = raw_asa , generated = True )
97+
98+
99+ def render_html_advisory (advisory , package , group , raw_asa , generated ):
100+ return render_template ('advisory.html' ,
101+ title = '[{}] {}: {}' .format (advisory .id , package .pkgname , advisory .advisory_type ),
102+ advisory = advisory ,
103+ package = package ,
104+ raw_asa = raw_asa ,
105+ generated = generated ,
106+ can_handle_advisory = user_can_handle_advisory (),
107+ Publication = Publication )
15108
16109
17110def advisory_fetch_from_mailman (url ):
18111 try :
19112 response = get (url )
20113 if 200 != response .status_code :
21114 return None
22- asa = unescape (sub ('</?A[^<]*?>' , '' , response .text ))
23- start = '<PRE>'
24- start_marker = '{}Arch Linux Security Advisory' .format (start )
25- end = '\n -------------- next part --------------'
26- asa = asa [asa .index (start_marker ) + len (start ):asa .index (end )]
27- return asa .strip ()
115+
116+ return response .text
28117 except Exception :
29118 return None
30119
@@ -33,18 +122,29 @@ def advisory_fetch_reference_url_from_mailman(advisory):
33122 try :
34123 year = advisory .id [4 :8 ]
35124 month = advisory .id [8 :10 ]
36- publish_date = date ( int ( year ), int ( month ), 1 )
37- mailman_url = '{}{}-{}/' . format ( TRACKER_MAILMAN_URL , year , publish_date . strftime ( '%B' ))
38- response = get (mailman_url )
125+ mailman_monthly = '{}{}/{}/?count=100' . format ( TRACKER_MAILMAN_URL , year , month )
126+
127+ response = get (mailman_monthly )
39128 if 200 != response .status_code :
40129 return None
130+
131+ mailman_url = urlparse (TRACKER_MAILMAN_URL )
132+ thread_url_base = join (mailman_url .path , 'thread' )
133+
134+ message_url = None
41135 for line in response .text .split ('\n ' ):
136+ if thread_url_base in line :
137+ match = search (r'href="{}/([/a-zA-Z0-9]+)"' .format (thread_url_base ), line )
138+ if not match :
139+ continue
140+
141+ thread = match .group (1 )
142+ message_url = join (TRACKER_MAILMAN_URL , 'message' , thread )
143+
42144 if not '[{}]' .format (advisory .id ) in line :
43145 continue
44- match = search (r'HREF="(\d+.html)"' , line )
45- if not match :
46- continue
47- return '{}{}' .format (mailman_url , match .group (1 ))
146+
147+ return message_url
48148 return None
49149 except Exception :
50150 return None
@@ -96,14 +196,6 @@ def advisory_extend_html(advisory, issues, package):
96196 return advisory
97197
98198
99- def advisory_extend_model_from_advisory_text (advisory ):
100- if not advisory .content :
101- return advisory
102- advisory .impact = advisory_get_impact_from_text (advisory .content )
103- advisory .workaround = advisory_get_workaround_from_text (advisory .content )
104- return advisory
105-
106-
107199def advisory_get_date_label (utctimetuple = None ):
108200 now = utctimetuple if utctimetuple else datetime .utcnow ().utctimetuple ()
109201 return '{}{:02}' .format (now .tm_year , now .tm_mon )
0 commit comments