greynoise 2.2.0__tar.gz → 2.3.0__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 (47) hide show
  1. {greynoise-2.2.0 → greynoise-2.3.0}/CHANGELOG.rst +18 -1
  2. {greynoise-2.2.0/src/greynoise.egg-info → greynoise-2.3.0}/PKG-INFO +19 -2
  3. {greynoise-2.2.0 → greynoise-2.3.0}/setup.py +1 -1
  4. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/__version__.py +1 -1
  5. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/api/__init__.py +25 -0
  6. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/decorator.py +38 -2
  7. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/formatter.py +9 -0
  8. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/subcommand.py +20 -0
  9. greynoise-2.3.0/src/greynoise/cli/templates/cvedetails.txt.j2 +53 -0
  10. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/util.py +19 -0
  11. {greynoise-2.2.0 → greynoise-2.3.0/src/greynoise.egg-info}/PKG-INFO +19 -2
  12. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise.egg-info/SOURCES.txt +1 -0
  13. {greynoise-2.2.0 → greynoise-2.3.0}/LICENSE +0 -0
  14. {greynoise-2.2.0 → greynoise-2.3.0}/MANIFEST.in +0 -0
  15. {greynoise-2.2.0 → greynoise-2.3.0}/README.rst +0 -0
  16. {greynoise-2.2.0 → greynoise-2.3.0}/setup.cfg +0 -0
  17. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/__init__.py +0 -0
  18. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/api/filter.py +0 -0
  19. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/__init__.py +0 -0
  20. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/helper.py +0 -0
  21. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/parameter.py +0 -0
  22. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/analyze.txt.j2 +0 -0
  23. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/gnql_query.txt.j2 +0 -0
  24. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/gnql_stats.txt.j2 +0 -0
  25. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/interesting.txt.j2 +0 -0
  26. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/ip_community.txt.j2 +0 -0
  27. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/ip_context.txt.j2 +0 -0
  28. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/ip_context_result.txt.j2 +0 -0
  29. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/ip_multi_context.txt.j2 +0 -0
  30. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/ip_quick_check.txt.j2 +0 -0
  31. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/macros.txt.j2 +0 -0
  32. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/personadetails.txt.j2 +0 -0
  33. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/riot.txt.j2 +0 -0
  34. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/sensoractivity.txt.j2 +0 -0
  35. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/sensorlist.txt.j2 +0 -0
  36. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/similarity.txt.j2 +0 -0
  37. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/stats.txt.j2 +0 -0
  38. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/timeline.txt.j2 +0 -0
  39. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/cli/templates/timelinehourly.txt.j2 +0 -0
  40. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise/exceptions.py +0 -0
  41. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise.egg-info/dependency_links.txt +0 -0
  42. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise.egg-info/entry_points.txt +0 -0
  43. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise.egg-info/not-zip-safe +0 -0
  44. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise.egg-info/requires.txt +0 -0
  45. {greynoise-2.2.0 → greynoise-2.3.0}/src/greynoise.egg-info/top_level.txt +0 -0
  46. {greynoise-2.2.0 → greynoise-2.3.0}/tests/test_api.py +0 -0
  47. {greynoise-2.2.0 → greynoise-2.3.0}/tests/test_util.py +0 -0
@@ -2,6 +2,22 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version `2.3.0`_
6
+ ================
7
+ **Date**: July 30, 2024
8
+
9
+ * API client:
10
+
11
+ * Add `cve` command to query the CVE lookup API
12
+
13
+ * CLI:
14
+
15
+ * Add `cve` command to display result from CVE lookup API
16
+
17
+ * Dependencies:
18
+
19
+ * Updated cachetools to version 5.4.0
20
+
5
21
  Version `2.2.0`_
6
22
  ================
7
23
  **Date**: June 11, 2024
@@ -412,4 +428,5 @@ Version `0.2.0`_
412
428
  .. _`2.0.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v1.3.0...2.0.0
413
429
  .. _`2.0.1`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.0.0...2.0.1
414
430
  .. _`2.1.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.0.1...2.1.0
415
- .. _`2.2.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.1.0...HEAD
431
+ .. _`2.2.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.1.0...2.2.0
432
+ .. _`2.3.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.2.0...HEAD
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: greynoise
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Abstraction to interact with GreyNoise API.
5
5
  Home-page: https://greynoise.io/
6
6
  Download-URL: https://github.com/GreyNoise-Intelligence/pygreynoise
@@ -124,6 +124,22 @@ Usage
124
124
  Changelog
125
125
  =========
126
126
 
127
+ Version `2.3.0`_
128
+ ================
129
+ **Date**: July 30, 2024
130
+
131
+ * API client:
132
+
133
+ * Add `cve` command to query the CVE lookup API
134
+
135
+ * CLI:
136
+
137
+ * Add `cve` command to display result from CVE lookup API
138
+
139
+ * Dependencies:
140
+
141
+ * Updated cachetools to version 5.4.0
142
+
127
143
  Version `2.2.0`_
128
144
  ================
129
145
  **Date**: June 11, 2024
@@ -534,4 +550,5 @@ Version `0.2.0`_
534
550
  .. _`2.0.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v1.3.0...2.0.0
535
551
  .. _`2.0.1`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.0.0...2.0.1
536
552
  .. _`2.1.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.0.1...2.1.0
537
- .. _`2.2.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.1.0...HEAD
553
+ .. _`2.2.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.1.0...2.2.0
554
+ .. _`2.3.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.2.0...HEAD
@@ -27,7 +27,7 @@ INSTALL_REQUIRES = [
27
27
 
28
28
  setup(
29
29
  name="greynoise",
30
- version="2.2.0",
30
+ version="2.3.0",
31
31
  description="Abstraction to interact with GreyNoise API.",
32
32
  url="https://greynoise.io/",
33
33
  author="GreyNoise Intelligence",
@@ -5,4 +5,4 @@ __license__ = "MIT"
5
5
  __maintainer__ = "GreyNoise Intelligence"
6
6
  __email__ = "hello@greynoise.io"
7
7
  __status__ = "BETA"
8
- __version__ = "2.2.0"
8
+ __version__ = "2.3.0"
@@ -15,6 +15,7 @@ from greynoise.api.filter import Filter
15
15
  from greynoise.exceptions import RateLimitError, RequestFailure
16
16
  from greynoise.util import (
17
17
  load_config,
18
+ validate_cve_id,
18
19
  validate_ip,
19
20
  validate_similar_min_score,
20
21
  validate_timeline_days,
@@ -61,6 +62,7 @@ class GreyNoise(object): # pylint: disable=R0205,R0902
61
62
  EP_SENSOR_ACTIVITY = "v1/workspaces/{workspace_id}/sensors/activity"
62
63
  EP_SENSOR_LIST = "v1/workspaces/{workspace_id}/sensors"
63
64
  EP_PERSONA_DETAILS = "v1/personas/{persona_id}"
65
+ EP_CVE_LOOKUP = "v1/cve/{cve_id}"
64
66
  EP_ANALYZE_UPLOAD = "v2/analyze/upload"
65
67
  EP_ANALYZE = "v2/analyze/{id}"
66
68
  EP_NOT_IMPLEMENTED = "v2/request/{subcommand}"
@@ -962,3 +964,26 @@ class GreyNoise(object): # pylint: disable=R0205,R0902
962
964
  response = self._request(endpoint)
963
965
 
964
966
  return response
967
+
968
+ def cve(self, cve_id=None):
969
+ """Get CVE details by CVE ID
970
+
971
+ :param cve_id: ID of CVE
972
+ :type cve_id: str
973
+
974
+
975
+ """
976
+ if self.offering == "community":
977
+ response = {
978
+ "message": "CVE lookup is not supported with Community offering"
979
+ }
980
+ else:
981
+ LOGGER.debug("Getting Details for CVE ID: %s...", cve_id)
982
+
983
+ # check if CVE submitted is in correct format
984
+ validate_cve_id(cve_id)
985
+
986
+ endpoint = self.EP_CVE_LOOKUP.format(cve_id=cve_id)
987
+ response = self._request(endpoint)
988
+
989
+ return response
@@ -240,7 +240,7 @@ def not_implemented_command(function):
240
240
 
241
241
 
242
242
  def workspace_command(function):
243
- """Decorator that groups decorators common to sensor activity subcommands."""
243
+ """Decorator that groups decorators common to workspace subcommands."""
244
244
 
245
245
  @click.command()
246
246
  @click.argument("workspace_id", required=True)
@@ -323,7 +323,7 @@ def sensor_activity_command(function):
323
323
 
324
324
 
325
325
  def persona_command(function):
326
- """Decorator that groups decorators common to sensor activity subcommands."""
326
+ """Decorator that groups decorators common to persona subcommands."""
327
327
 
328
328
  @click.command()
329
329
  @click.argument("persona_id", required=True)
@@ -356,3 +356,39 @@ def persona_command(function):
356
356
  return function(*args, **kwargs)
357
357
 
358
358
  return wrapper
359
+
360
+
361
+ def cve_command(function):
362
+ """Decorator that groups decorators common to cve subcommand."""
363
+
364
+ @click.command()
365
+ @click.argument("cve_id", required=True)
366
+ @click.option("-k", "--api-key", help="Key to include in API requests")
367
+ @click.option(
368
+ "-O",
369
+ "--offering",
370
+ help="Which API offering to use, enterprise or community, "
371
+ "defaults to enterprise",
372
+ )
373
+ @click.option("-i", "--input", "input_file", type=click.File(), help="Input file")
374
+ @click.option(
375
+ "-o", "--output", "output_file", type=click.File(mode="w"), help="Output file"
376
+ )
377
+ @click.option(
378
+ "-f",
379
+ "--format",
380
+ "output_format",
381
+ type=click.Choice(["json", "txt", "xml"]),
382
+ default="txt",
383
+ help="Output format",
384
+ )
385
+ @click.option("-v", "--verbose", count=True, help="Verbose output")
386
+ @pass_api_client
387
+ @click.pass_context
388
+ @echo_result
389
+ @handle_exceptions
390
+ @functools.wraps(function)
391
+ def wrapper(*args, **kwargs):
392
+ return function(*args, **kwargs)
393
+
394
+ return wrapper
@@ -218,6 +218,14 @@ def personadetails_formatter(results, verbose):
218
218
  return template.render(results=results, verbose=verbose, max_width=max_width)
219
219
 
220
220
 
221
+ @colored_output
222
+ def cvedetails_formatter(results, verbose):
223
+ """Convert CVE Details to human-readable text."""
224
+ template = JINJA2_ENV.get_template("cvedetails.txt.j2")
225
+ max_width, _ = shutil.get_terminal_size()
226
+ return template.render(results=results, verbose=verbose, max_width=max_width)
227
+
228
+
221
229
  FORMATTERS = {
222
230
  "json": json_formatter,
223
231
  "xml": xml_formatter,
@@ -237,5 +245,6 @@ FORMATTERS = {
237
245
  "sensor-activity": sensoractivity_formatter,
238
246
  "sensor-list": sensorlist_formatter,
239
247
  "persona-details": personadetails_formatter,
248
+ "cve": cvedetails_formatter,
240
249
  },
241
250
  }
@@ -7,6 +7,7 @@ import click
7
7
 
8
8
  from greynoise.__version__ import __version__
9
9
  from greynoise.cli.decorator import (
10
+ cve_command,
10
11
  echo_result,
11
12
  gnql_command,
12
13
  handle_exceptions,
@@ -497,3 +498,22 @@ def persona_details(
497
498
  persona_id=persona_id,
498
499
  )
499
500
  return result
501
+
502
+
503
+ @cve_command
504
+ def cve(
505
+ context,
506
+ api_client,
507
+ api_key,
508
+ input_file,
509
+ output_file,
510
+ output_format,
511
+ verbose,
512
+ cve_id,
513
+ offering,
514
+ ):
515
+ """Retrieve Details of a CVE."""
516
+ result = api_client.cve(
517
+ cve_id=cve_id,
518
+ )
519
+ return result
@@ -0,0 +1,53 @@
1
+ {% import "macros.txt.j2" as macros with context %}
2
+ {%- if results.details %}
3
+ ----------------------------
4
+ <header>CVE Details</header>
5
+ ----------------------------
6
+ <key>CVE</key>: <value>{{ results.id }}</value>
7
+ <key>Vuln Name</key>: <value>{{ results.details.vulnerability_name }}</value>
8
+ <key>Vuln Description</key>: <value>{{ results.details.vulnerability_description }}</value>
9
+ <key>Vendor</key>: <value>{{ results.details.vendor }}</value>
10
+ <key>Product</key>: <value>{{ results.details.product }}</value>
11
+ <key>CVSS Score</key>: <value>{{ results.details.cve_cvss_score }}</value>
12
+ <key>Published to NIST NVD</key>: <value>{{ results.details.published_to_nist_nvd }}</value>
13
+
14
+ {% if results.timeline -%}
15
+ <header>Timeline Information</header>
16
+ --------------------
17
+ <key>Published Date</key>: <value>{{ results.timeline.cve_published_date.split("T")[0] }}</value>
18
+ <key>Last Updated Date</key>: <value>{{ results.timeline.cve_last_updated_date.split("T")[0] }}</value>
19
+ <key>First Known Date</key>: <value>{{ results.timeline.first_known_published_date.split("T")[0] }}</value>
20
+ <key>Added to CISA Kev Date</key>: <value>{{ results.timeline.cisa_kev_date_added.split("T")[0] }}</value>
21
+ {%- endif %}
22
+
23
+ {% if results.exploitation_details -%}
24
+ <header>Exploitation Details</header>
25
+ --------------------
26
+ <key>Attack Vector</key>: <value>{{ results.exploitation_details.attack_vector }}</value>
27
+ <key>Exploit Found</key>: <value>{{ results.exploitation_details.exploit_found }}</value>
28
+ <key>Exploit in KEV</key>: <value>{{ results.exploitation_details.exploitation_registered_in_kev }}</value>
29
+ <key>EPSS Score</key>: <value>{{ results.exploitation_details.epss_score }}</value>
30
+ {%- endif %}
31
+
32
+ {% if results.exploitation_stats -%}
33
+ <header>Exploitation Stats</header>
34
+ ------------------
35
+ <key># of Exploits</key>: <value>{{ results.exploitation_stats.number_of_available_exploits }}</value>
36
+ <key># of Threat Actors</key>: <value>{{ results.exploitation_stats.number_of_threat_actors_exploiting_vulnerability }}</value>
37
+ <key># of Botnets </key>: <value>{{ results.exploitation_stats.number_of_botnets_exploiting_vulnerability }}</value>
38
+ {%- endif %}
39
+
40
+ {% if results.exploitation_activity -%}
41
+ <header>Exploitation Activity</header>
42
+ ---------------------
43
+ <key>Activity Seen</key>: <value>{{ results.exploitation_activity.activity_seen }}</value>
44
+ <key># Benign Scanners - 1 Day</key>: <value>{{ results.exploitation_activity.benign_ip_count_1d }}</value>
45
+ <key># Benign Scanners - 10 Days</key>: <value>{{ results.exploitation_activity.benign_ip_count_10d }}</value>
46
+ <key># Benign Scanners - 30 Days</key>: <value>{{ results.exploitation_activity.benign_ip_count_30d }}</value>
47
+ <key># Suspicious Scanners - 1 Day</key>: <value>{{ results.exploitation_activity.threat_ip_count_1d }}</value>
48
+ <key># Suspicious Scanners - 10 Day</key>: <value>{{ results.exploitation_activity.threat_ip_count_10d }}</value>
49
+ <key># Suspicious Scanners - 30 Day</key>: <value>{{ results.exploitation_activity.threat_ip_count_30d }}</value>
50
+ {%- endif %}
51
+ {% else %}
52
+ Provided CVE was not found or valid.
53
+ {%- endif %}
@@ -1,6 +1,7 @@
1
1
  """Utility functions."""
2
2
  import logging
3
3
  import os
4
+ import re
4
5
  from ipaddress import IPv6Address, ip_address
5
6
 
6
7
  from six.moves.configparser import ConfigParser
@@ -228,3 +229,21 @@ def validate_similar_min_score(min_score):
228
229
  return True
229
230
  else:
230
231
  raise ValueError("Min Score must be a valid integer between 0 and 100.")
232
+
233
+
234
+ def validate_cve_id(cve_id):
235
+ """Check if provided value is a valid CVE ID
236
+
237
+ :param cve_id: field value to validate.
238
+ :type cve_id: str
239
+
240
+ """
241
+ # CVE regular expression
242
+ cve_pattern = r"CVE-\d{4}-\d{4,7}"
243
+
244
+ pattern = re.compile(cve_pattern)
245
+
246
+ if not pattern.match(cve_id):
247
+ raise ValueError("The provided ID does not match the format: CVE-XXXX-YYYYY")
248
+ else:
249
+ return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: greynoise
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Abstraction to interact with GreyNoise API.
5
5
  Home-page: https://greynoise.io/
6
6
  Download-URL: https://github.com/GreyNoise-Intelligence/pygreynoise
@@ -124,6 +124,22 @@ Usage
124
124
  Changelog
125
125
  =========
126
126
 
127
+ Version `2.3.0`_
128
+ ================
129
+ **Date**: July 30, 2024
130
+
131
+ * API client:
132
+
133
+ * Add `cve` command to query the CVE lookup API
134
+
135
+ * CLI:
136
+
137
+ * Add `cve` command to display result from CVE lookup API
138
+
139
+ * Dependencies:
140
+
141
+ * Updated cachetools to version 5.4.0
142
+
127
143
  Version `2.2.0`_
128
144
  ================
129
145
  **Date**: June 11, 2024
@@ -534,4 +550,5 @@ Version `0.2.0`_
534
550
  .. _`2.0.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v1.3.0...2.0.0
535
551
  .. _`2.0.1`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.0.0...2.0.1
536
552
  .. _`2.1.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.0.1...2.1.0
537
- .. _`2.2.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.1.0...HEAD
553
+ .. _`2.2.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.1.0...2.2.0
554
+ .. _`2.3.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.2.0...HEAD
@@ -24,6 +24,7 @@ src/greynoise/cli/helper.py
24
24
  src/greynoise/cli/parameter.py
25
25
  src/greynoise/cli/subcommand.py
26
26
  src/greynoise/cli/templates/analyze.txt.j2
27
+ src/greynoise/cli/templates/cvedetails.txt.j2
27
28
  src/greynoise/cli/templates/gnql_query.txt.j2
28
29
  src/greynoise/cli/templates/gnql_stats.txt.j2
29
30
  src/greynoise/cli/templates/interesting.txt.j2
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes