greynoise 3.0.1__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.1 → greynoise-3.0.3}/.bumpversion.cfg +1 -1
  2. {greynoise-3.0.1 → greynoise-3.0.3}/.circleci/config.yml +0 -5
  3. {greynoise-3.0.1 → greynoise-3.0.3}/CHANGELOG.rst +18 -1
  4. {greynoise-3.0.1 → greynoise-3.0.3}/PKG-INFO +1 -1
  5. {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/conf.py +1 -1
  6. {greynoise-3.0.1 → greynoise-3.0.3}/pyproject.toml +1 -1
  7. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/__version__.py +1 -1
  8. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/api/__init__.py +46 -4
  9. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/subcommand.py +11 -1
  10. {greynoise-3.0.1 → greynoise-3.0.3}/tests/cli/test_subcommand.py +30 -3
  11. {greynoise-3.0.1 → greynoise-3.0.3}/tests/test_api.py +29 -3
  12. {greynoise-3.0.1 → greynoise-3.0.3}/.dockerignore +0 -0
  13. {greynoise-3.0.1 → greynoise-3.0.3}/.editorconfig +0 -0
  14. {greynoise-3.0.1 → greynoise-3.0.3}/.github/CODEOWNERS +0 -0
  15. {greynoise-3.0.1 → greynoise-3.0.3}/.github/dependabot.yml +0 -0
  16. {greynoise-3.0.1 → greynoise-3.0.3}/.github/workflows/slack_release_notice.yml +0 -0
  17. {greynoise-3.0.1 → greynoise-3.0.3}/.gitignore +0 -0
  18. {greynoise-3.0.1 → greynoise-3.0.3}/.pre-commit-config.yaml +0 -0
  19. {greynoise-3.0.1 → greynoise-3.0.3}/.readthedocs.yml +0 -0
  20. {greynoise-3.0.1 → greynoise-3.0.3}/.yamllint +0 -0
  21. {greynoise-3.0.1 → greynoise-3.0.3}/Dockerfile +0 -0
  22. {greynoise-3.0.1 → greynoise-3.0.3}/LICENSE +0 -0
  23. {greynoise-3.0.1 → greynoise-3.0.3}/MANIFEST.in +0 -0
  24. {greynoise-3.0.1 → greynoise-3.0.3}/Makefile +0 -0
  25. {greynoise-3.0.1 → greynoise-3.0.3}/README.rst +0 -0
  26. {greynoise-3.0.1 → greynoise-3.0.3}/docs/Makefile +0 -0
  27. {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/changelog.rst +0 -0
  28. {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/index.rst +0 -0
  29. {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/introduction.rst +0 -0
  30. {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/reference.rst +0 -0
  31. {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/sample.py +0 -0
  32. {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/tutorial.rst +0 -0
  33. {greynoise-3.0.1 → greynoise-3.0.3}/pytest.ini +0 -0
  34. {greynoise-3.0.1 → greynoise-3.0.3}/release.md +0 -0
  35. {greynoise-3.0.1 → greynoise-3.0.3}/requirements/common.txt +0 -0
  36. {greynoise-3.0.1 → greynoise-3.0.3}/requirements/dev.txt +0 -0
  37. {greynoise-3.0.1 → greynoise-3.0.3}/requirements/docs.txt +0 -0
  38. {greynoise-3.0.1 → greynoise-3.0.3}/requirements/test.txt +0 -0
  39. {greynoise-3.0.1 → greynoise-3.0.3}/reset_changelog.py +0 -0
  40. {greynoise-3.0.1 → greynoise-3.0.3}/setup.cfg +0 -0
  41. {greynoise-3.0.1 → greynoise-3.0.3}/sonar-project.properties +0 -0
  42. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/__init__.py +0 -0
  43. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/api/filter.py +0 -0
  44. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/__init__.py +0 -0
  45. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/decorator.py +0 -0
  46. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/formatter.py +0 -0
  47. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/helper.py +0 -0
  48. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/parameter.py +0 -0
  49. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/analyze.txt.j2 +0 -0
  50. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/cvedetails.txt.j2 +0 -0
  51. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/gnql_query.txt.j2 +0 -0
  52. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/gnql_stats.txt.j2 +0 -0
  53. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_community.txt.j2 +0 -0
  54. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_context.txt.j2 +0 -0
  55. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_context_result.txt.j2 +0 -0
  56. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_multi_context.txt.j2 +0 -0
  57. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_quick_check.txt.j2 +0 -0
  58. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/macros.txt.j2 +0 -0
  59. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/personadetails.txt.j2 +0 -0
  60. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/riot.txt.j2 +0 -0
  61. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/sensoractivity.txt.j2 +0 -0
  62. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/sensorlist.txt.j2 +0 -0
  63. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/similarity.txt.j2 +0 -0
  64. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/stats.txt.j2 +0 -0
  65. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/timeline.txt.j2 +0 -0
  66. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/timelinehourly.txt.j2 +0 -0
  67. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/exceptions.py +0 -0
  68. {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/util.py +0 -0
  69. {greynoise-3.0.1 → greynoise-3.0.3}/tests/cli/test_formatter.py +0 -0
  70. {greynoise-3.0.1 → greynoise-3.0.3}/tests/cli/test_init.py +0 -0
  71. {greynoise-3.0.1 → greynoise-3.0.3}/tests/test_util.py +0 -0
  72. {greynoise-3.0.1 → greynoise-3.0.3}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 3.0.1
2
+ current_version = 3.0.3
3
3
  tag = False
4
4
  commit = False
5
5
 
@@ -24,11 +24,6 @@ workflows:
24
24
  - py312:
25
25
  filters:
26
26
  <<: *TAGS_FILTER
27
- - sonarcloud:
28
- requires:
29
- # generate static analysis and coverage reports in py37 job
30
- # and upload them to sonarcloud.io in sonarcloud job
31
- - py39
32
27
  - pypi:
33
28
  requires:
34
29
  # release to pypi only when test cases pass in all python versions
@@ -2,6 +2,21 @@
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
+
13
+ Version `3.0.2`_
14
+ ================
15
+ **Date**: March 10, 2026
16
+
17
+ * API client:
18
+ * Update `proxy` parameter to be used for all API requests
19
+
5
20
  Version `3.0.1`_
6
21
  ================
7
22
  **Date**: June 18, 2025
@@ -475,4 +490,6 @@ Version `0.2.0`_
475
490
  .. _`2.2.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.1.0...2.2.0
476
491
  .. _`2.3.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.2.0...2.3.0
477
492
  .. _`3.0.0`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v2.3.0...v3.0.0
478
- .. _`3.0.1`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v3.0.0...HEAD
493
+ .. _`3.0.1`: https://github.com/GreyNoise-Intelligence/pygreynoise/compare/v3.0.0...v3.0.1
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.1
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.1"
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.1"
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.1"
8
+ __version__ = "3.0.3"
@@ -98,6 +98,9 @@ class BaseAPIClient:
98
98
  if params is None:
99
99
  params = {}
100
100
 
101
+ # Use config proxy when no proxy is explicitly passed
102
+ proxy = proxy or self.config.proxy
103
+
101
104
  user_agent_parts = ["GreyNoise/{}".format(__version__)]
102
105
  if self.config.integration_name:
103
106
  user_agent_parts.append("({})".format(self.config.integration_name))
@@ -246,7 +249,7 @@ class GreyNoise(BaseAPIClient):
246
249
  NAME = "GreyNoise"
247
250
  EP_GNQL = "v3/gnql"
248
251
  EP_GNQL_METADATA = "v3/gnql/metadata"
249
- EP_GNQL_STATS = "v2/experimental/gnql/stats"
252
+ EP_GNQL_STATS = "v3/gnql/stats"
250
253
  EP_IP = "v3/ip/{ip_address}"
251
254
  EP_NOISE_MULTI = "v3/ip?quick=true"
252
255
  EP_NOISE_CONTEXT_MULTI = "v3/ip"
@@ -433,19 +436,58 @@ class GreyNoise(BaseAPIClient):
433
436
  response = self._request(endpoint)
434
437
  return response
435
438
 
436
- def query(self, query, size=None, scroll=None, exclude_raw=False, quick=False):
437
- """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
+ """
438
464
  if self.offering == "community":
439
465
  response = {"message": "GNQL not supported with Community offering"}
440
466
  else:
441
467
  LOGGER.debug(
442
- "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,
443
474
  )
444
475
  params = {"query": query, "quick": quick}
445
476
  if size is not None:
446
477
  params["size"] = size
447
478
  if scroll is not None:
448
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
449
491
  if exclude_raw:
450
492
  LOGGER.debug("Using GNQL Metadata endpoint")
451
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