greynoise 3.0.2__tar.gz → 3.0.3__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 (72) hide show
  1. {greynoise-3.0.2 → greynoise-3.0.3}/.bumpversion.cfg +1 -1
  2. {greynoise-3.0.2 → greynoise-3.0.3}/CHANGELOG.rst +10 -1
  3. {greynoise-3.0.2 → greynoise-3.0.3}/PKG-INFO +1 -1
  4. {greynoise-3.0.2 → greynoise-3.0.3}/docs/source/conf.py +1 -1
  5. {greynoise-3.0.2 → greynoise-3.0.3}/pyproject.toml +1 -1
  6. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/__version__.py +1 -1
  7. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/api/__init__.py +43 -4
  8. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/subcommand.py +11 -1
  9. {greynoise-3.0.2 → greynoise-3.0.3}/tests/cli/test_subcommand.py +30 -3
  10. {greynoise-3.0.2 → greynoise-3.0.3}/tests/test_api.py +29 -3
  11. {greynoise-3.0.2 → greynoise-3.0.3}/.circleci/config.yml +0 -0
  12. {greynoise-3.0.2 → greynoise-3.0.3}/.dockerignore +0 -0
  13. {greynoise-3.0.2 → greynoise-3.0.3}/.editorconfig +0 -0
  14. {greynoise-3.0.2 → greynoise-3.0.3}/.github/CODEOWNERS +0 -0
  15. {greynoise-3.0.2 → greynoise-3.0.3}/.github/dependabot.yml +0 -0
  16. {greynoise-3.0.2 → greynoise-3.0.3}/.github/workflows/slack_release_notice.yml +0 -0
  17. {greynoise-3.0.2 → greynoise-3.0.3}/.gitignore +0 -0
  18. {greynoise-3.0.2 → greynoise-3.0.3}/.pre-commit-config.yaml +0 -0
  19. {greynoise-3.0.2 → greynoise-3.0.3}/.readthedocs.yml +0 -0
  20. {greynoise-3.0.2 → greynoise-3.0.3}/.yamllint +0 -0
  21. {greynoise-3.0.2 → greynoise-3.0.3}/Dockerfile +0 -0
  22. {greynoise-3.0.2 → greynoise-3.0.3}/LICENSE +0 -0
  23. {greynoise-3.0.2 → greynoise-3.0.3}/MANIFEST.in +0 -0
  24. {greynoise-3.0.2 → greynoise-3.0.3}/Makefile +0 -0
  25. {greynoise-3.0.2 → greynoise-3.0.3}/README.rst +0 -0
  26. {greynoise-3.0.2 → greynoise-3.0.3}/docs/Makefile +0 -0
  27. {greynoise-3.0.2 → greynoise-3.0.3}/docs/source/changelog.rst +0 -0
  28. {greynoise-3.0.2 → greynoise-3.0.3}/docs/source/index.rst +0 -0
  29. {greynoise-3.0.2 → greynoise-3.0.3}/docs/source/introduction.rst +0 -0
  30. {greynoise-3.0.2 → greynoise-3.0.3}/docs/source/reference.rst +0 -0
  31. {greynoise-3.0.2 → greynoise-3.0.3}/docs/source/sample.py +0 -0
  32. {greynoise-3.0.2 → greynoise-3.0.3}/docs/source/tutorial.rst +0 -0
  33. {greynoise-3.0.2 → greynoise-3.0.3}/pytest.ini +0 -0
  34. {greynoise-3.0.2 → greynoise-3.0.3}/release.md +0 -0
  35. {greynoise-3.0.2 → greynoise-3.0.3}/requirements/common.txt +0 -0
  36. {greynoise-3.0.2 → greynoise-3.0.3}/requirements/dev.txt +0 -0
  37. {greynoise-3.0.2 → greynoise-3.0.3}/requirements/docs.txt +0 -0
  38. {greynoise-3.0.2 → greynoise-3.0.3}/requirements/test.txt +0 -0
  39. {greynoise-3.0.2 → greynoise-3.0.3}/reset_changelog.py +0 -0
  40. {greynoise-3.0.2 → greynoise-3.0.3}/setup.cfg +0 -0
  41. {greynoise-3.0.2 → greynoise-3.0.3}/sonar-project.properties +0 -0
  42. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/__init__.py +0 -0
  43. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/api/filter.py +0 -0
  44. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/__init__.py +0 -0
  45. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/decorator.py +0 -0
  46. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/formatter.py +0 -0
  47. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/helper.py +0 -0
  48. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/parameter.py +0 -0
  49. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/analyze.txt.j2 +0 -0
  50. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/cvedetails.txt.j2 +0 -0
  51. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/gnql_query.txt.j2 +0 -0
  52. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/gnql_stats.txt.j2 +0 -0
  53. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_community.txt.j2 +0 -0
  54. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_context.txt.j2 +0 -0
  55. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_context_result.txt.j2 +0 -0
  56. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_multi_context.txt.j2 +0 -0
  57. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_quick_check.txt.j2 +0 -0
  58. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/macros.txt.j2 +0 -0
  59. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/personadetails.txt.j2 +0 -0
  60. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/riot.txt.j2 +0 -0
  61. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/sensoractivity.txt.j2 +0 -0
  62. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/sensorlist.txt.j2 +0 -0
  63. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/similarity.txt.j2 +0 -0
  64. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/stats.txt.j2 +0 -0
  65. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/timeline.txt.j2 +0 -0
  66. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/cli/templates/timelinehourly.txt.j2 +0 -0
  67. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/exceptions.py +0 -0
  68. {greynoise-3.0.2 → greynoise-3.0.3}/src/greynoise/util.py +0 -0
  69. {greynoise-3.0.2 → greynoise-3.0.3}/tests/cli/test_formatter.py +0 -0
  70. {greynoise-3.0.2 → greynoise-3.0.3}/tests/cli/test_init.py +0 -0
  71. {greynoise-3.0.2 → greynoise-3.0.3}/tests/test_util.py +0 -0
  72. {greynoise-3.0.2 → greynoise-3.0.3}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 3.0.2
2
+ current_version = 3.0.3
3
3
  tag = False
4
4
  commit = False
5
5
 
@@ -2,6 +2,14 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version `3.0.3`_
6
+ ================
7
+ **Date**: May 15, 2026
8
+
9
+ * API client:
10
+ * Add `exclude_fields` parameter to `query` command
11
+ * Update `stats` to use v3 endpoint
12
+
5
13
  Version `3.0.2`_
6
14
  ================
7
15
  **Date**: March 10, 2026
@@ -483,4 +491,5 @@ Version `0.2.0`_
483
491
  .. _`2.3.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.2.0...2.3.0
484
492
  .. _`3.0.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.3.0...v3.0.0
485
493
  .. _`3.0.1`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v3.0.0...v3.0.1
486
- .. _`3.0.2`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v3.0.1...HEAD
494
+ .. _`3.0.2`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v3.0.1...v3.0.2
495
+ .. _`3.0.3`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v3.0.2...HEAD
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: greynoise
3
- Version: 3.0.2
3
+ Version: 3.0.3
4
4
  Summary: Abstraction to interact with GreyNoise API.
5
5
  Project-URL: Homepage, https://greynoise.io/
6
6
  Project-URL: Repository, https://github.com/GreyNoise-Intelligence/pygreynoise
@@ -25,7 +25,7 @@ copyright = "2018-2025, GreyNoise Intelligence"
25
25
  author = "GreyNoise Intelligence"
26
26
 
27
27
  # The full version, including alpha/beta/rc tags
28
- release = "3.0.2"
28
+ release = "3.0.3"
29
29
 
30
30
 
31
31
  # -- General configuration ---------------------------------------------------
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "greynoise"
7
- version = "3.0.2"
7
+ version = "3.0.3"
8
8
  description = "Abstraction to interact with GreyNoise API."
9
9
  readme = {file = "README.rst", content-type = "text/x-rst"}
10
10
  requires-python = ">=3.8"
@@ -5,4 +5,4 @@ __license__ = "MIT"
5
5
  __maintainer__ = "GreyNoise Intelligence"
6
6
  __email__ = "support@greynoise.io"
7
7
  __status__ = "BETA"
8
- __version__ = "3.0.2"
8
+ __version__ = "3.0.3"
@@ -249,7 +249,7 @@ class GreyNoise(BaseAPIClient):
249
249
  NAME = "GreyNoise"
250
250
  EP_GNQL = "v3/gnql"
251
251
  EP_GNQL_METADATA = "v3/gnql/metadata"
252
- EP_GNQL_STATS = "v2/experimental/gnql/stats"
252
+ EP_GNQL_STATS = "v3/gnql/stats"
253
253
  EP_IP = "v3/ip/{ip_address}"
254
254
  EP_NOISE_MULTI = "v3/ip?quick=true"
255
255
  EP_NOISE_CONTEXT_MULTI = "v3/ip"
@@ -436,19 +436,58 @@ class GreyNoise(BaseAPIClient):
436
436
  response = self._request(endpoint)
437
437
  return response
438
438
 
439
- def query(self, query, size=None, scroll=None, exclude_raw=False, quick=False):
440
- """Run GNQL query."""
439
+ def query(
440
+ self,
441
+ query,
442
+ size=None,
443
+ scroll=None,
444
+ exclude_raw=False,
445
+ quick=False,
446
+ exclude_fields: Optional[Union[str, List[str]]] = None,
447
+ ) -> Dict[str, Any]:
448
+ """Run GNQL query.
449
+
450
+ :param query: GNQL query
451
+ :type query: str
452
+ :param size: Max number of results to return
453
+ :type size: int
454
+ :param scroll: Scroll token for pagination
455
+ :type scroll: str
456
+ :param exclude_raw: Whether to exclude raw results
457
+ :type exclude_raw: bool
458
+ :param quick: Whether to use quick lookup
459
+ :type quick: bool
460
+ :param exclude_fields: Field names to exclude; comma-separated string or list
461
+ of strings (sent to the API as the ``exclude`` query parameter).
462
+ :type exclude_fields: str or list[str]
463
+ """
441
464
  if self.offering == "community":
442
465
  response = {"message": "GNQL not supported with Community offering"}
443
466
  else:
444
467
  LOGGER.debug(
445
- "Running GNQL query: %s %s %s %s...", query, size, scroll, quick
468
+ "Running GNQL query: %s %s %s %s %s...",
469
+ query,
470
+ size,
471
+ scroll,
472
+ quick,
473
+ exclude_fields,
446
474
  )
447
475
  params = {"query": query, "quick": quick}
448
476
  if size is not None:
449
477
  params["size"] = size
450
478
  if scroll is not None:
451
479
  params["scroll"] = scroll
480
+ if exclude_fields is not None:
481
+ if isinstance(exclude_fields, str):
482
+ normalized_ef = ",".join(
483
+ p.strip() for p in exclude_fields.split(",") if p.strip()
484
+ )
485
+ else:
486
+ normalized_ef = ",".join(
487
+ str(p).strip() for p in exclude_fields if str(p).strip()
488
+ )
489
+ if normalized_ef:
490
+ params["exclude"] = normalized_ef
452
491
  if exclude_raw:
453
492
  LOGGER.debug("Using GNQL Metadata endpoint")
454
493
  endpoint = self.EP_GNQL_METADATA
@@ -170,6 +170,12 @@ def riot(
170
170
 
171
171
 
172
172
  @gnql_command
173
+ @click.option(
174
+ "--exclude",
175
+ "exclude_fields",
176
+ default=None,
177
+ help="Comma-separated fields to omit from GNQL results (API ``exclude`` param).",
178
+ )
173
179
  def query(
174
180
  context,
175
181
  api_client,
@@ -182,11 +188,15 @@ def query(
182
188
  size,
183
189
  scroll,
184
190
  offering,
191
+ exclude_fields,
185
192
  ):
186
193
  """Run a GNQL (GreyNoise Query Language) query."""
187
194
  queries = get_queries(context, input_file, query)
188
195
  results = [
189
- api_client.query(query=item, size=size, scroll=scroll) for item in queries
196
+ api_client.query(
197
+ query=item, size=size, scroll=scroll, exclude_fields=exclude_fields
198
+ )
199
+ for item in queries
190
200
  ]
191
201
  return results
192
202
 
@@ -585,7 +585,9 @@ class TestQuery(object):
585
585
  result = runner.invoke(subcommand.query, ["-f", "json", query])
586
586
  assert result.exit_code == 0
587
587
  assert result.output.strip("\n") == expected
588
- api_client.query.assert_called_with(query=query, size=None, scroll=None)
588
+ api_client.query.assert_called_with(
589
+ query=query, size=None, scroll=None, exclude_fields=None
590
+ )
589
591
 
590
592
  def test_input_file(self, api_client):
591
593
  """Run query from input file."""
@@ -598,7 +600,9 @@ class TestQuery(object):
598
600
  result = runner.invoke(subcommand.query, ["-f", "json", "-i", StringIO(query)])
599
601
  assert result.exit_code == 0
600
602
  assert result.output.strip("\n") == expected
601
- api_client.query.assert_called_with(query=query, size=None, scroll=None)
603
+ api_client.query.assert_called_with(
604
+ query=query, size=None, scroll=None, exclude_fields=None
605
+ )
602
606
 
603
607
  def test_stdin_input(self, api_client):
604
608
  """Run query from stdin."""
@@ -611,7 +615,30 @@ class TestQuery(object):
611
615
  result = runner.invoke(subcommand.query, ["-f", "json"], input=query)
612
616
  assert result.exit_code == 0
613
617
  assert result.output.strip("\n") == expected
614
- api_client.query.assert_called_with(query=query, size=None, scroll=None)
618
+ api_client.query.assert_called_with(
619
+ query=query, size=None, scroll=None, exclude_fields=None
620
+ )
621
+
622
+ def test_query_with_exclude(self, api_client):
623
+ """Run query with --exclude forwarded to the client."""
624
+ runner = CliRunner()
625
+
626
+ query = "<query>"
627
+ api_client.query.return_value = []
628
+ expected = json.dumps([[]], indent=4, sort_keys=True)
629
+
630
+ result = runner.invoke(
631
+ subcommand.query,
632
+ ["-f", "json", "--exclude", "raw_data,tls", query],
633
+ )
634
+ assert result.exit_code == 0
635
+ assert result.output.strip("\n") == expected
636
+ api_client.query.assert_called_with(
637
+ query=query,
638
+ size=None,
639
+ scroll=None,
640
+ exclude_fields="raw_data,tls",
641
+ )
615
642
 
616
643
  def test_no_query_passed(self, api_client):
617
644
  """Usage is returned if no query or input file is passed."""
@@ -727,6 +727,34 @@ class TestQuery(object):
727
727
  )
728
728
  assert response == expected_response
729
729
 
730
+ def test_query_exclude_fields_as_list(self, client):
731
+ """exclude_fields is sent as a comma-separated exclude query parameter."""
732
+ query = "<query>"
733
+ client._request = Mock(return_value=[])
734
+ client.query(query, exclude_fields=["raw_data", "tls"])
735
+ client._request.assert_called_with(
736
+ "v3/gnql",
737
+ params={
738
+ "query": query,
739
+ "quick": False,
740
+ "exclude": "raw_data,tls",
741
+ },
742
+ )
743
+
744
+ def test_query_exclude_fields_as_string(self, client):
745
+ """exclude_fields string is normalized to comma-separated names."""
746
+ query = "<query>"
747
+ client._request = Mock(return_value=[])
748
+ client.query(query, exclude_fields=" raw_data , tls ")
749
+ client._request.assert_called_with(
750
+ "v3/gnql",
751
+ params={
752
+ "query": query,
753
+ "quick": False,
754
+ "exclude": "raw_data,tls",
755
+ },
756
+ )
757
+
730
758
 
731
759
  class TestStats(object):
732
760
  """GreyNoise client run GNQL stats query test cases."""
@@ -738,9 +766,7 @@ class TestStats(object):
738
766
 
739
767
  client._request = Mock(return_value=expected_response)
740
768
  response = client.stats(query)
741
- client._request.assert_called_with(
742
- "v2/experimental/gnql/stats", params={"query": query}
743
- )
769
+ client._request.assert_called_with("v3/gnql/stats", params={"query": query})
744
770
  assert response == expected_response
745
771
 
746
772
 
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