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.
Files changed (35) hide show
  1. {advisory_parser-1.13/advisory_parser.egg-info → advisory_parser-1.14}/PKG-INFO +12 -2
  2. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/__init__.py +1 -2
  3. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/chrome.py +20 -7
  4. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/utils.py +19 -9
  5. {advisory_parser-1.13 → advisory_parser-1.14/advisory_parser.egg-info}/PKG-INFO +12 -2
  6. {advisory_parser-1.13 → advisory_parser-1.14}/setup.py +1 -2
  7. {advisory_parser-1.13 → advisory_parser-1.14}/tox.ini +1 -1
  8. {advisory_parser-1.13 → advisory_parser-1.14}/COPYRIGHT +0 -0
  9. {advisory_parser-1.13 → advisory_parser-1.14}/LICENSE +0 -0
  10. {advisory_parser-1.13 → advisory_parser-1.14}/MANIFEST.in +0 -0
  11. {advisory_parser-1.13 → advisory_parser-1.14}/README.rst +0 -0
  12. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/exceptions.py +0 -0
  13. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/flaw.py +0 -0
  14. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parser.py +0 -0
  15. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/__init__.py +0 -0
  16. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/flash.py +0 -0
  17. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/jenkins.py +0 -0
  18. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser/parsers/mysql.py +0 -0
  19. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser.egg-info/SOURCES.txt +0 -0
  20. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser.egg-info/dependency_links.txt +0 -0
  21. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser.egg-info/requires.txt +0 -0
  22. {advisory_parser-1.13 → advisory_parser-1.14}/advisory_parser.egg-info/top_level.txt +0 -0
  23. {advisory_parser-1.13 → advisory_parser-1.14}/pyproject.toml +0 -0
  24. {advisory_parser-1.13 → advisory_parser-1.14}/setup.cfg +0 -0
  25. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_chrome_parser.py +0 -0
  26. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/chrome_2017-06-15.txt +0 -0
  27. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/chrome_2020-02-04.txt +0 -0
  28. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/flash_apsb17-21.html +0 -0
  29. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/flash_apsb17-23.html +0 -0
  30. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/flash_apsb17-33.html +0 -0
  31. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/jenkins_2023-04-12.html +0 -0
  32. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_data/mysql_cpu-apr-2019.html +0 -0
  33. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_flash_parser.py +0 -0
  34. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_jenkins_parser.py +0 -0
  35. {advisory_parser-1.13 → advisory_parser-1.14}/tests/test_mysql_parser.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: advisory-parser
3
- Version: 1.13
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,5 +4,4 @@
4
4
 
5
5
  from .parser import Parser
6
6
 
7
-
8
- __version__ = "1.13"
7
+ __version__ = "1.14"
@@ -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 "Security Fixes" not in advisory_text:
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("Labels:\nStable updates")[0].strip()
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), "%A, %B %d, %Y")
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(r"\d{2,3}\.\d\.\d{4}\.\d{2,3}", flaws_text).group(0)
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
- print("Could not find impact; skipping: {}".format(line))
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 += "\nhttps://code.google.com/p/chromium/issues/detail?id=" + bug
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
- user_agent = (
18
- "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7"
19
- )
20
- headers = {"User-Agent": user_agent}
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 res.read()
43
+ return data
34
44
 
35
45
 
36
46
  def get_text_from_url(url):
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: advisory-parser
3
- Version: 1.13
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.13",
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",
@@ -1,5 +1,5 @@
1
1
  [tox]
2
- envlist = black,py36,py37,py38,py39
2
+ envlist = black,py313
3
3
 
4
4
  [testenv]
5
5
  deps = pytest
File without changes
File without changes
File without changes