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.
- {greynoise-3.0.1 → greynoise-3.0.3}/.bumpversion.cfg +1 -1
- {greynoise-3.0.1 → greynoise-3.0.3}/.circleci/config.yml +0 -5
- {greynoise-3.0.1 → greynoise-3.0.3}/CHANGELOG.rst +18 -1
- {greynoise-3.0.1 → greynoise-3.0.3}/PKG-INFO +1 -1
- {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/conf.py +1 -1
- {greynoise-3.0.1 → greynoise-3.0.3}/pyproject.toml +1 -1
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/__version__.py +1 -1
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/api/__init__.py +46 -4
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/subcommand.py +11 -1
- {greynoise-3.0.1 → greynoise-3.0.3}/tests/cli/test_subcommand.py +30 -3
- {greynoise-3.0.1 → greynoise-3.0.3}/tests/test_api.py +29 -3
- {greynoise-3.0.1 → greynoise-3.0.3}/.dockerignore +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/.editorconfig +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/.github/CODEOWNERS +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/.github/dependabot.yml +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/.github/workflows/slack_release_notice.yml +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/.gitignore +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/.pre-commit-config.yaml +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/.readthedocs.yml +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/.yamllint +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/Dockerfile +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/LICENSE +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/MANIFEST.in +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/Makefile +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/README.rst +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/docs/Makefile +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/changelog.rst +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/index.rst +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/introduction.rst +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/reference.rst +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/sample.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/docs/source/tutorial.rst +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/pytest.ini +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/release.md +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/requirements/common.txt +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/requirements/dev.txt +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/requirements/docs.txt +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/requirements/test.txt +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/reset_changelog.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/setup.cfg +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/sonar-project.properties +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/__init__.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/api/filter.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/__init__.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/decorator.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/formatter.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/helper.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/parameter.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/analyze.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/cvedetails.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/gnql_query.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/gnql_stats.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_community.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_context.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_context_result.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_multi_context.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/ip_quick_check.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/macros.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/personadetails.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/riot.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/sensoractivity.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/sensorlist.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/similarity.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/stats.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/timeline.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/cli/templates/timelinehourly.txt.j2 +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/exceptions.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/src/greynoise/util.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/tests/cli/test_formatter.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/tests/cli/test_init.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/tests/test_util.py +0 -0
- {greynoise-3.0.1 → greynoise-3.0.3}/tox.ini +0 -0
|
@@ -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...
|
|
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
|
|
@@ -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.
|
|
28
|
+
release = "3.0.3"
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
# -- General configuration ---------------------------------------------------
|
|
@@ -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 = "
|
|
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(
|
|
437
|
-
|
|
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...",
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|