advisory-parser 1.13__tar.gz → 1.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {advisory_parser-1.13/advisory_parser.egg-info → advisory_parser-1.14}/PKG-INFO +12 -2
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/__init__.py +1 -2
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/chrome.py +20 -7
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/utils.py +19 -9
- {advisory_parser-1.13 → advisory_parser-1.14/advisory_parser.egg-info}/PKG-INFO +12 -2
- {advisory_parser-1.13 → advisory_parser-1.14}/setup.py +1 -2
- {advisory_parser-1.13 → advisory_parser-1.14}/tox.ini +1 -1
- {advisory_parser-1.13 → advisory_parser-1.14}/COPYRIGHT +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/LICENSE +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/MANIFEST.in +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/README.rst +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/exceptions.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/flaw.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parser.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/__init__.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/flash.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/jenkins.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/mysql.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser.egg-info/SOURCES.txt +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser.egg-info/dependency_links.txt +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser.egg-info/requires.txt +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser.egg-info/top_level.txt +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/pyproject.toml +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/setup.cfg +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_chrome_parser.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/chrome_2017-06-15.txt +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/chrome_2020-02-04.txt +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/flash_apsb17-21.html +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/flash_apsb17-23.html +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/flash_apsb17-33.html +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/jenkins_2023-04-12.html +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/mysql_cpu-apr-2019.html +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_flash_parser.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_jenkins_parser.py +0 -0
- {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_mysql_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: advisory-parser
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14
|
|
4
4
|
Summary: Security flaw parser for upstream security advisories
|
|
5
5
|
Home-page: https://github.com/RedHatProductSecurity/advisory-parser
|
|
6
6
|
Author: Martin Prpič, Red Hat Product Security
|
|
@@ -22,6 +22,16 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: beautifulsoup4>=4.0.0
|
|
24
24
|
Requires-Dist: cvss>=2.0
|
|
25
|
+
Dynamic: author
|
|
26
|
+
Dynamic: author-email
|
|
27
|
+
Dynamic: classifier
|
|
28
|
+
Dynamic: description
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: keywords
|
|
31
|
+
Dynamic: license
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: summary
|
|
25
35
|
|
|
26
36
|
Advisory Parser
|
|
27
37
|
===============
|
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
# License: LGPLv3+
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
+
import logging
|
|
6
7
|
from datetime import datetime
|
|
7
8
|
|
|
8
9
|
from advisory_parser.exceptions import AdvisoryParserTextException
|
|
9
10
|
from advisory_parser.flaw import Flaw
|
|
10
11
|
from .utils import get_text_from_url, CVE_REGEX
|
|
11
12
|
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
12
15
|
# Chromium does not publish CVSS scores with their CVEs so these values are
|
|
13
16
|
# best-effort guesses based on impact.
|
|
14
17
|
CVSS3_MAP = {
|
|
@@ -18,6 +21,13 @@ CVSS3_MAP = {
|
|
|
18
21
|
"low": "4.3/CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L",
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
# Constants for Chrome advisory parsing
|
|
25
|
+
SECURITY_FIXES_HEADER = "Security Fixes"
|
|
26
|
+
LABELS_FOOTER = "Labels:\nStable updates"
|
|
27
|
+
DATE_FORMAT = "%A, %B %d, %Y"
|
|
28
|
+
CHROME_VERSION_REGEX = r"\d{2,3}\.\d\.\d{4}\.\d{2,3}"
|
|
29
|
+
CHROMIUM_ISSUE_BASE_URL = "https://code.google.com/p/chromium/issues/detail?id="
|
|
30
|
+
|
|
21
31
|
|
|
22
32
|
def parse_chrome_advisory(url):
|
|
23
33
|
advisory_text = get_text_from_url(url)
|
|
@@ -25,12 +35,15 @@ def parse_chrome_advisory(url):
|
|
|
25
35
|
# Workaround for advisories that do not use <div>s for each CVE entry. E.g.:
|
|
26
36
|
# https://chromereleases.googleblog.com/2018/04/stable-channel-update-for-desktop.html
|
|
27
37
|
advisory_text = re.sub(r"(.)\[\$", r"\1\n[$", advisory_text)
|
|
38
|
+
# 2026+ posts concatenate CVE entries on one line using [TBD][bugid],
|
|
39
|
+
# [N/A][bugid], or [$reward][bugid]. Split them into separate lines.
|
|
40
|
+
advisory_text = re.sub(r"(?<=\S)(\[(?:TBD|N/A|\$\d+)\]\[)", r"\n\1", advisory_text)
|
|
28
41
|
|
|
29
|
-
if
|
|
42
|
+
if SECURITY_FIXES_HEADER not in advisory_text:
|
|
30
43
|
raise AdvisoryParserTextException("No security fixes found in {}".format(url))
|
|
31
44
|
|
|
32
45
|
# Throw away parts of the text after the blog post
|
|
33
|
-
flaws_text = advisory_text.split(
|
|
46
|
+
flaws_text = advisory_text.split(LABELS_FOOTER)[0].strip()
|
|
34
47
|
|
|
35
48
|
# Parse out public date
|
|
36
49
|
match = re.search("^Stable Channel Update for Desktop\n(.+)", flaws_text, re.MULTILINE)
|
|
@@ -38,7 +51,7 @@ def parse_chrome_advisory(url):
|
|
|
38
51
|
raise AdvisoryParserTextException("Could not find public date in {}".format(url))
|
|
39
52
|
|
|
40
53
|
try:
|
|
41
|
-
public_date = datetime.strptime(match.group(1),
|
|
54
|
+
public_date = datetime.strptime(match.group(1), DATE_FORMAT)
|
|
42
55
|
except ValueError:
|
|
43
56
|
raise AdvisoryParserTextException(
|
|
44
57
|
"Could not parse public date ({}) from {}".format(match.group(1), url)
|
|
@@ -46,8 +59,8 @@ def parse_chrome_advisory(url):
|
|
|
46
59
|
|
|
47
60
|
# Find Chrome version, e.g. 46.0.2490.71
|
|
48
61
|
try:
|
|
49
|
-
fixed_in = re.search(
|
|
50
|
-
except ValueError:
|
|
62
|
+
fixed_in = re.search(CHROME_VERSION_REGEX, flaws_text).group(0)
|
|
63
|
+
except (AttributeError, ValueError):
|
|
51
64
|
raise AdvisoryParserTextException("Could not find fixed-in version in {}".format(url))
|
|
52
65
|
|
|
53
66
|
# Filter out lines that contain CVEs
|
|
@@ -72,7 +85,7 @@ def parse_chrome_advisory(url):
|
|
|
72
85
|
else:
|
|
73
86
|
match = re.search(r"(Critical|High|Medium|Low)", metadata)
|
|
74
87
|
if not match:
|
|
75
|
-
|
|
88
|
+
logger.warning("Could not find impact; skipping: %s", line)
|
|
76
89
|
continue
|
|
77
90
|
else:
|
|
78
91
|
impact = match.group(1)
|
|
@@ -110,7 +123,7 @@ def parse_chrome_advisory(url):
|
|
|
110
123
|
|
|
111
124
|
description += "\n\nUpstream bug(s):\n"
|
|
112
125
|
for bug in bug_ids:
|
|
113
|
-
description += "\
|
|
126
|
+
description += "\n" + CHROMIUM_ISSUE_BASE_URL + bug
|
|
114
127
|
|
|
115
128
|
com_url = (
|
|
116
129
|
url if "blogspot.com" in url else re.sub(r"blogspot\.[^/]*/", "blogspot.com/", url)
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
# License: LGPLv3+
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
+
import gzip
|
|
7
|
+
import logging
|
|
6
8
|
from urllib.error import HTTPError, URLError
|
|
7
9
|
from urllib.request import urlopen, Request
|
|
8
10
|
|
|
@@ -10,27 +12,35 @@ from bs4 import BeautifulSoup
|
|
|
10
12
|
|
|
11
13
|
from advisory_parser.exceptions import AdvisoryParserGetContentException
|
|
12
14
|
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
13
17
|
CVE_REGEX = re.compile(r"CVE-\d{4}-\d{4,}")
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
def get_request(url):
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
headers = {
|
|
22
|
+
"User-Agent": "Advisory-Parser/1.0",
|
|
23
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
24
|
+
"Accept-Encoding": "gzip, deflate",
|
|
25
|
+
}
|
|
21
26
|
request = Request(url, None, headers)
|
|
22
27
|
try:
|
|
23
|
-
res = urlopen(request)
|
|
28
|
+
res = urlopen(request, timeout=30)
|
|
29
|
+
data = res.read()
|
|
30
|
+
|
|
31
|
+
# Handle gzip-compressed responses
|
|
32
|
+
if res.headers.get("Content-Encoding") == "gzip":
|
|
33
|
+
data = gzip.decompress(data)
|
|
24
34
|
except HTTPError as e:
|
|
25
|
-
error_msg = "Failed to GET with status code: {}".format(e.code)
|
|
35
|
+
error_msg = "Failed to GET {} with status code: {}".format(url, e.code)
|
|
26
36
|
raise AdvisoryParserGetContentException(error_msg)
|
|
27
37
|
except URLError as e:
|
|
28
|
-
error_msg = "Failed to establish connection: {}".format(e.reason)
|
|
38
|
+
error_msg = "Failed to establish connection to {}: {}".format(url, e.reason)
|
|
29
39
|
raise AdvisoryParserGetContentException(error_msg)
|
|
30
40
|
except ValueError:
|
|
31
|
-
raise AdvisoryParserGetContentException("Invalid URL specified.
|
|
41
|
+
raise AdvisoryParserGetContentException("Invalid URL specified: {}".format(url))
|
|
32
42
|
else:
|
|
33
|
-
return
|
|
43
|
+
return data
|
|
34
44
|
|
|
35
45
|
|
|
36
46
|
def get_text_from_url(url):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: advisory-parser
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14
|
|
4
4
|
Summary: Security flaw parser for upstream security advisories
|
|
5
5
|
Home-page: https://github.com/RedHatProductSecurity/advisory-parser
|
|
6
6
|
Author: Martin Prpič, Red Hat Product Security
|
|
@@ -22,6 +22,16 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: beautifulsoup4>=4.0.0
|
|
24
24
|
Requires-Dist: cvss>=2.0
|
|
25
|
+
Dynamic: author
|
|
26
|
+
Dynamic: author-email
|
|
27
|
+
Dynamic: classifier
|
|
28
|
+
Dynamic: description
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: keywords
|
|
31
|
+
Dynamic: license
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: summary
|
|
25
35
|
|
|
26
36
|
Advisory Parser
|
|
27
37
|
===============
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
from setuptools import setup
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
with open("README.rst", "r") as f:
|
|
9
8
|
description = f.read()
|
|
10
9
|
|
|
@@ -12,7 +11,7 @@ requires = ["beautifulsoup4>=4.0.0", "cvss>=2.0"]
|
|
|
12
11
|
|
|
13
12
|
setup(
|
|
14
13
|
name="advisory-parser",
|
|
15
|
-
version="1.
|
|
14
|
+
version="1.14",
|
|
16
15
|
description="Security flaw parser for upstream security advisories",
|
|
17
16
|
long_description=description,
|
|
18
17
|
url="https://github.com/RedHatProductSecurity/advisory-parser",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|