contrastapi 1.22.3__py3-none-any.whl
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.
- contrastapi/__init__.py +39 -0
- contrastapi/_transport.py +170 -0
- contrastapi/_version.py +1 -0
- contrastapi/client.py +171 -0
- contrastapi/exceptions.py +176 -0
- contrastapi/models.py +661 -0
- contrastapi/namespaces/__init__.py +0 -0
- contrastapi/namespaces/atlas.py +134 -0
- contrastapi/namespaces/cve.py +125 -0
- contrastapi/namespaces/d3fend.py +124 -0
- contrastapi/namespaces/domain.py +129 -0
- contrastapi/namespaces/intel.py +151 -0
- contrastapi/namespaces/ioc.py +61 -0
- contrastapi/namespaces/security.py +94 -0
- contrastapi/shortcuts.py +182 -0
- contrastapi-1.22.3.dist-info/METADATA +194 -0
- contrastapi-1.22.3.dist-info/RECORD +18 -0
- contrastapi-1.22.3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""ATLAS namespace — MITRE ATLAS AI/ML attack catalog (techniques + case studies)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from urllib.parse import quote
|
|
7
|
+
|
|
8
|
+
from .._transport import _AsyncTransport, _SyncTransport
|
|
9
|
+
from ..models import (
|
|
10
|
+
AtlasCaseStudyResponse,
|
|
11
|
+
AtlasCaseStudySearchResponse,
|
|
12
|
+
AtlasTechniqueResponse,
|
|
13
|
+
AtlasTechniqueSearchResponse,
|
|
14
|
+
BulkAtlasTechniqueResponse,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _enc(value: str) -> str:
|
|
19
|
+
if value is None or value == "":
|
|
20
|
+
raise ValueError("Missing required parameter")
|
|
21
|
+
return quote(str(value), safe="")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _resolve_keyword(keyword: str | None, q: str | None) -> str | None:
|
|
25
|
+
"""Server param is `keyword`; SDK accepts `keyword=` or back-compat `q=` (Node parity)."""
|
|
26
|
+
if keyword is not None and q is not None:
|
|
27
|
+
raise ValueError("Pass only one of `keyword` or `q` (q is a back-compat alias)")
|
|
28
|
+
return keyword if keyword is not None else q
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _technique_search_params(
|
|
32
|
+
*,
|
|
33
|
+
keyword: str | None = None,
|
|
34
|
+
q: str | None = None,
|
|
35
|
+
tactic: str | None = None,
|
|
36
|
+
maturity: str | None = None,
|
|
37
|
+
limit: int | None = None,
|
|
38
|
+
offset: int | None = None,
|
|
39
|
+
include: str | None = None,
|
|
40
|
+
exclude_id: str | None = None,
|
|
41
|
+
) -> dict[str, Any]:
|
|
42
|
+
params: dict[str, Any] = {}
|
|
43
|
+
kw = _resolve_keyword(keyword, q)
|
|
44
|
+
if kw is not None:
|
|
45
|
+
params["keyword"] = kw
|
|
46
|
+
if tactic is not None:
|
|
47
|
+
params["tactic"] = tactic
|
|
48
|
+
if maturity is not None:
|
|
49
|
+
params["maturity"] = maturity
|
|
50
|
+
if limit is not None:
|
|
51
|
+
params["limit"] = limit
|
|
52
|
+
if offset is not None:
|
|
53
|
+
params["offset"] = offset
|
|
54
|
+
if include is not None:
|
|
55
|
+
params["include"] = include
|
|
56
|
+
if exclude_id is not None:
|
|
57
|
+
params["exclude_id"] = exclude_id
|
|
58
|
+
return params
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _case_study_search_params(
|
|
62
|
+
*,
|
|
63
|
+
keyword: str | None = None,
|
|
64
|
+
q: str | None = None,
|
|
65
|
+
target_type: str | None = None,
|
|
66
|
+
limit: int | None = None,
|
|
67
|
+
offset: int | None = None,
|
|
68
|
+
include: str | None = None,
|
|
69
|
+
) -> dict[str, Any]:
|
|
70
|
+
params: dict[str, Any] = {}
|
|
71
|
+
kw = _resolve_keyword(keyword, q)
|
|
72
|
+
if kw is not None:
|
|
73
|
+
params["keyword"] = kw
|
|
74
|
+
if target_type is not None:
|
|
75
|
+
params["target_type"] = target_type
|
|
76
|
+
if limit is not None:
|
|
77
|
+
params["limit"] = limit
|
|
78
|
+
if offset is not None:
|
|
79
|
+
params["offset"] = offset
|
|
80
|
+
if include is not None:
|
|
81
|
+
params["include"] = include
|
|
82
|
+
return params
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _validate_bulk(ids: list[str]) -> list[str]:
|
|
86
|
+
if not isinstance(ids, list) or not all(isinstance(i, str) for i in ids):
|
|
87
|
+
raise ValueError("technique_ids must be a list of strings")
|
|
88
|
+
return ids
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Atlas:
|
|
92
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
93
|
+
self._t = transport
|
|
94
|
+
|
|
95
|
+
def technique(self, technique_id: str) -> AtlasTechniqueResponse:
|
|
96
|
+
return self._t.get(f"/v1/atlas/{_enc(technique_id)}")
|
|
97
|
+
|
|
98
|
+
def technique_search(self, **kwargs: Any) -> AtlasTechniqueSearchResponse:
|
|
99
|
+
return self._t.get("/v1/atlas/techniques", params=_technique_search_params(**kwargs))
|
|
100
|
+
|
|
101
|
+
def bulk_technique_lookup(self, technique_ids: list[str]) -> BulkAtlasTechniqueResponse:
|
|
102
|
+
return self._t.post(
|
|
103
|
+
"/v1/atlas/techniques/bulk",
|
|
104
|
+
json_body={"technique_ids": _validate_bulk(technique_ids)},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def case_study(self, case_study_id: str) -> AtlasCaseStudyResponse:
|
|
108
|
+
return self._t.get(f"/v1/atlas/case-studies/{_enc(case_study_id)}")
|
|
109
|
+
|
|
110
|
+
def case_study_search(self, **kwargs: Any) -> AtlasCaseStudySearchResponse:
|
|
111
|
+
return self._t.get("/v1/atlas/case-studies", params=_case_study_search_params(**kwargs))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class AsyncAtlas:
|
|
115
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
116
|
+
self._t = transport
|
|
117
|
+
|
|
118
|
+
async def technique(self, technique_id: str) -> AtlasTechniqueResponse:
|
|
119
|
+
return await self._t.aget(f"/v1/atlas/{_enc(technique_id)}")
|
|
120
|
+
|
|
121
|
+
async def technique_search(self, **kwargs: Any) -> AtlasTechniqueSearchResponse:
|
|
122
|
+
return await self._t.aget("/v1/atlas/techniques", params=_technique_search_params(**kwargs))
|
|
123
|
+
|
|
124
|
+
async def bulk_technique_lookup(self, technique_ids: list[str]) -> BulkAtlasTechniqueResponse:
|
|
125
|
+
return await self._t.apost(
|
|
126
|
+
"/v1/atlas/techniques/bulk",
|
|
127
|
+
json_body={"technique_ids": _validate_bulk(technique_ids)},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
async def case_study(self, case_study_id: str) -> AtlasCaseStudyResponse:
|
|
131
|
+
return await self._t.aget(f"/v1/atlas/case-studies/{_enc(case_study_id)}")
|
|
132
|
+
|
|
133
|
+
async def case_study_search(self, **kwargs: Any) -> AtlasCaseStudySearchResponse:
|
|
134
|
+
return await self._t.aget("/v1/atlas/case-studies", params=_case_study_search_params(**kwargs))
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""CVE namespace — vulnerabilities, KEV, CWE, exploits, bulk lookup, leading list."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from urllib.parse import quote
|
|
7
|
+
|
|
8
|
+
from .._transport import _AsyncTransport, _SyncTransport
|
|
9
|
+
from ..models import (
|
|
10
|
+
BulkCveResponse,
|
|
11
|
+
CveResponse,
|
|
12
|
+
CveSearchResponse,
|
|
13
|
+
CweLookupResponse,
|
|
14
|
+
ExploitResponse,
|
|
15
|
+
KevDetailResponse,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _enc(value: str) -> str:
|
|
20
|
+
if value is None or value == "":
|
|
21
|
+
raise ValueError("Missing required parameter")
|
|
22
|
+
return quote(str(value), safe="")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _search_params(
|
|
26
|
+
*,
|
|
27
|
+
product: str | None = None,
|
|
28
|
+
severity: str | None = None,
|
|
29
|
+
days: int | None = None,
|
|
30
|
+
limit: int | None = None,
|
|
31
|
+
) -> dict[str, Any]:
|
|
32
|
+
params: dict[str, Any] = {}
|
|
33
|
+
if product is not None:
|
|
34
|
+
params["product"] = product
|
|
35
|
+
if severity is not None:
|
|
36
|
+
params["severity"] = severity
|
|
37
|
+
if days is not None:
|
|
38
|
+
params["days"] = days
|
|
39
|
+
if limit is not None:
|
|
40
|
+
params["limit"] = limit
|
|
41
|
+
return params
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _leading_params(
|
|
45
|
+
*,
|
|
46
|
+
limit: int | None = None,
|
|
47
|
+
offset: int | None = None,
|
|
48
|
+
include: str | None = None,
|
|
49
|
+
) -> dict[str, Any]:
|
|
50
|
+
params: dict[str, Any] = {}
|
|
51
|
+
if limit is not None:
|
|
52
|
+
params["limit"] = limit
|
|
53
|
+
if offset is not None:
|
|
54
|
+
params["offset"] = offset
|
|
55
|
+
if include is not None:
|
|
56
|
+
params["include"] = include
|
|
57
|
+
return params
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _validate_bulk_ids(ids: list[str]) -> list[str]:
|
|
61
|
+
if not isinstance(ids, list) or not all(isinstance(c, str) for c in ids):
|
|
62
|
+
raise ValueError("cve_ids must be a list of strings")
|
|
63
|
+
return ids
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Cve:
|
|
67
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
68
|
+
self._t = transport
|
|
69
|
+
|
|
70
|
+
def lookup(self, cve_id: str) -> CveResponse:
|
|
71
|
+
return self._t.get(f"/v1/cve/{_enc(cve_id)}")
|
|
72
|
+
|
|
73
|
+
def search(self, **kwargs: Any) -> CveSearchResponse:
|
|
74
|
+
return self._t.get("/v1/cves", params=_search_params(**kwargs))
|
|
75
|
+
|
|
76
|
+
def leading(self, **kwargs: Any) -> CveSearchResponse:
|
|
77
|
+
return self._t.get("/v1/cve/leading", params=_leading_params(**kwargs))
|
|
78
|
+
|
|
79
|
+
def kev(self, cve_id: str) -> KevDetailResponse:
|
|
80
|
+
return self._t.get(f"/v1/kev/{_enc(cve_id)}")
|
|
81
|
+
|
|
82
|
+
def exploit(self, cve_id: str) -> ExploitResponse:
|
|
83
|
+
return self._t.get(f"/v1/exploit/{_enc(cve_id)}")
|
|
84
|
+
|
|
85
|
+
def bulk(self, cve_ids: list[str]) -> BulkCveResponse:
|
|
86
|
+
return self._t.post("/v1/cves/bulk", json_body={"cve_ids": _validate_bulk_ids(cve_ids)})
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Cwe:
|
|
90
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
91
|
+
self._t = transport
|
|
92
|
+
|
|
93
|
+
def lookup(self, cwe_id: str) -> CweLookupResponse:
|
|
94
|
+
return self._t.get(f"/v1/cwe/{_enc(cwe_id)}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class AsyncCve:
|
|
98
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
99
|
+
self._t = transport
|
|
100
|
+
|
|
101
|
+
async def lookup(self, cve_id: str) -> CveResponse:
|
|
102
|
+
return await self._t.aget(f"/v1/cve/{_enc(cve_id)}")
|
|
103
|
+
|
|
104
|
+
async def search(self, **kwargs: Any) -> CveSearchResponse:
|
|
105
|
+
return await self._t.aget("/v1/cves", params=_search_params(**kwargs))
|
|
106
|
+
|
|
107
|
+
async def leading(self, **kwargs: Any) -> CveSearchResponse:
|
|
108
|
+
return await self._t.aget("/v1/cve/leading", params=_leading_params(**kwargs))
|
|
109
|
+
|
|
110
|
+
async def kev(self, cve_id: str) -> KevDetailResponse:
|
|
111
|
+
return await self._t.aget(f"/v1/kev/{_enc(cve_id)}")
|
|
112
|
+
|
|
113
|
+
async def exploit(self, cve_id: str) -> ExploitResponse:
|
|
114
|
+
return await self._t.aget(f"/v1/exploit/{_enc(cve_id)}")
|
|
115
|
+
|
|
116
|
+
async def bulk(self, cve_ids: list[str]) -> BulkCveResponse:
|
|
117
|
+
return await self._t.apost("/v1/cves/bulk", json_body={"cve_ids": _validate_bulk_ids(cve_ids)})
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class AsyncCwe:
|
|
121
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
122
|
+
self._t = transport
|
|
123
|
+
|
|
124
|
+
async def lookup(self, cwe_id: str) -> CweLookupResponse:
|
|
125
|
+
return await self._t.aget(f"/v1/cwe/{_enc(cwe_id)}")
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""D3FEND namespace — MITRE D3FEND defense catalog mapped to ATT&CK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from urllib.parse import quote
|
|
7
|
+
|
|
8
|
+
from .._transport import _AsyncTransport, _SyncTransport
|
|
9
|
+
from ..models import (
|
|
10
|
+
D3fendCoverageResponse,
|
|
11
|
+
D3fendDefenseResponse,
|
|
12
|
+
D3fendDefenseSearchResponse,
|
|
13
|
+
D3fendForAttackResponse,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _enc(value: str) -> str:
|
|
18
|
+
if value is None or value == "":
|
|
19
|
+
raise ValueError("Missing required parameter")
|
|
20
|
+
return quote(str(value), safe="")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _resolve_keyword(keyword: str | None, q: str | None) -> str | None:
|
|
24
|
+
"""Server param is `keyword`; SDK accepts `keyword=` or back-compat `q=` (Node parity).
|
|
25
|
+
Passing both raises so we don't silently pick a winner.
|
|
26
|
+
"""
|
|
27
|
+
if keyword is not None and q is not None:
|
|
28
|
+
raise ValueError("Pass only one of `keyword` or `q` (q is a back-compat alias)")
|
|
29
|
+
return keyword if keyword is not None else q
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _defense_search_params(
|
|
33
|
+
*,
|
|
34
|
+
keyword: str | None = None,
|
|
35
|
+
q: str | None = None,
|
|
36
|
+
tactic: str | None = None,
|
|
37
|
+
artifact: str | None = None,
|
|
38
|
+
limit: int | None = None,
|
|
39
|
+
offset: int | None = None,
|
|
40
|
+
include: str | None = None,
|
|
41
|
+
exclude_id: str | None = None,
|
|
42
|
+
) -> dict[str, Any]:
|
|
43
|
+
params: dict[str, Any] = {}
|
|
44
|
+
kw = _resolve_keyword(keyword, q)
|
|
45
|
+
if kw is not None:
|
|
46
|
+
params["keyword"] = kw
|
|
47
|
+
if tactic is not None:
|
|
48
|
+
params["tactic"] = tactic
|
|
49
|
+
if artifact is not None:
|
|
50
|
+
params["artifact"] = artifact
|
|
51
|
+
if limit is not None:
|
|
52
|
+
params["limit"] = limit
|
|
53
|
+
if offset is not None:
|
|
54
|
+
params["offset"] = offset
|
|
55
|
+
if include is not None:
|
|
56
|
+
params["include"] = include
|
|
57
|
+
if exclude_id is not None:
|
|
58
|
+
params["exclude_id"] = exclude_id
|
|
59
|
+
return params
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _for_attack_params(
|
|
63
|
+
*,
|
|
64
|
+
include: str | None = None,
|
|
65
|
+
exclude_id: str | None = None,
|
|
66
|
+
) -> dict[str, Any]:
|
|
67
|
+
params: dict[str, Any] = {}
|
|
68
|
+
if include is not None:
|
|
69
|
+
params["include"] = include
|
|
70
|
+
if exclude_id is not None:
|
|
71
|
+
params["exclude_id"] = exclude_id
|
|
72
|
+
return params
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _validate_attack_ids(ids: list[str]) -> list[str]:
|
|
76
|
+
if not isinstance(ids, list) or not all(isinstance(i, str) for i in ids):
|
|
77
|
+
raise ValueError("attack_technique_ids must be a list of strings")
|
|
78
|
+
return ids
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class D3fend:
|
|
82
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
83
|
+
self._t = transport
|
|
84
|
+
|
|
85
|
+
def defense(self, defense_id: str) -> D3fendDefenseResponse:
|
|
86
|
+
return self._t.get(f"/v1/d3fend/{_enc(defense_id)}")
|
|
87
|
+
|
|
88
|
+
def defense_search(self, **kwargs: Any) -> D3fendDefenseSearchResponse:
|
|
89
|
+
return self._t.get("/v1/d3fend/defenses", params=_defense_search_params(**kwargs))
|
|
90
|
+
|
|
91
|
+
def defense_for_attack(self, attack_technique_id: str, **kwargs: Any) -> D3fendForAttackResponse:
|
|
92
|
+
return self._t.get(
|
|
93
|
+
f"/v1/d3fend/attack/{_enc(attack_technique_id)}",
|
|
94
|
+
params=_for_attack_params(**kwargs),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def coverage(self, attack_technique_ids: list[str]) -> D3fendCoverageResponse:
|
|
98
|
+
return self._t.post(
|
|
99
|
+
"/v1/d3fend/coverage",
|
|
100
|
+
json_body={"attack_technique_ids": _validate_attack_ids(attack_technique_ids)},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class AsyncD3fend:
|
|
105
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
106
|
+
self._t = transport
|
|
107
|
+
|
|
108
|
+
async def defense(self, defense_id: str) -> D3fendDefenseResponse:
|
|
109
|
+
return await self._t.aget(f"/v1/d3fend/{_enc(defense_id)}")
|
|
110
|
+
|
|
111
|
+
async def defense_search(self, **kwargs: Any) -> D3fendDefenseSearchResponse:
|
|
112
|
+
return await self._t.aget("/v1/d3fend/defenses", params=_defense_search_params(**kwargs))
|
|
113
|
+
|
|
114
|
+
async def defense_for_attack(self, attack_technique_id: str, **kwargs: Any) -> D3fendForAttackResponse:
|
|
115
|
+
return await self._t.aget(
|
|
116
|
+
f"/v1/d3fend/attack/{_enc(attack_technique_id)}",
|
|
117
|
+
params=_for_attack_params(**kwargs),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
async def coverage(self, attack_technique_ids: list[str]) -> D3fendCoverageResponse:
|
|
121
|
+
return await self._t.apost(
|
|
122
|
+
"/v1/d3fend/coverage",
|
|
123
|
+
json_body={"attack_technique_ids": _validate_attack_ids(attack_technique_ids)},
|
|
124
|
+
)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Domain namespace — DNS, WHOIS, SSL, subdomains, tech, threat, monitor, vulns, audit, wayback."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from urllib.parse import quote
|
|
7
|
+
|
|
8
|
+
from .._transport import _AsyncTransport, _SyncTransport
|
|
9
|
+
from ..models import (
|
|
10
|
+
AuditResponse,
|
|
11
|
+
BulkDomainResponse,
|
|
12
|
+
CertsResponse,
|
|
13
|
+
DnsResponse,
|
|
14
|
+
DomainReportResponse,
|
|
15
|
+
SslResponse,
|
|
16
|
+
SubdomainsResponse,
|
|
17
|
+
TechResponse,
|
|
18
|
+
ThreatResponse,
|
|
19
|
+
WaybackResponse,
|
|
20
|
+
WhoisResponse,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _enc(value: str) -> str:
|
|
25
|
+
if value is None or value == "":
|
|
26
|
+
raise ValueError("Missing required parameter")
|
|
27
|
+
return quote(str(value), safe="")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _validate_domains(domains: list[str]) -> list[str]:
|
|
31
|
+
if not isinstance(domains, list) or not all(isinstance(d, str) for d in domains):
|
|
32
|
+
raise ValueError("domains must be a list of strings")
|
|
33
|
+
return domains
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _report_params(lite: bool) -> dict[str, Any] | None:
|
|
37
|
+
"""`lite=True` → {"lite": "true"}; `lite=False` → None (clean URL).
|
|
38
|
+
|
|
39
|
+
Server treats only the literal string `true` as truthy (lowercase).
|
|
40
|
+
"""
|
|
41
|
+
return {"lite": "true"} if lite else None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Domain:
|
|
45
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
46
|
+
self._t = transport
|
|
47
|
+
|
|
48
|
+
def report(self, domain: str, *, lite: bool = False) -> DomainReportResponse:
|
|
49
|
+
return self._t.get(f"/v1/domain/{_enc(domain)}", params=_report_params(lite))
|
|
50
|
+
|
|
51
|
+
def dns(self, domain: str) -> DnsResponse:
|
|
52
|
+
return self._t.get(f"/v1/dns/{_enc(domain)}")
|
|
53
|
+
|
|
54
|
+
def whois(self, domain: str) -> WhoisResponse:
|
|
55
|
+
return self._t.get(f"/v1/whois/{_enc(domain)}")
|
|
56
|
+
|
|
57
|
+
def subdomains(self, domain: str) -> SubdomainsResponse:
|
|
58
|
+
return self._t.get(f"/v1/subdomains/{_enc(domain)}")
|
|
59
|
+
|
|
60
|
+
def certs(self, domain: str) -> CertsResponse:
|
|
61
|
+
return self._t.get(f"/v1/certs/{_enc(domain)}")
|
|
62
|
+
|
|
63
|
+
def ssl(self, domain: str) -> SslResponse:
|
|
64
|
+
return self._t.get(f"/v1/ssl/{_enc(domain)}")
|
|
65
|
+
|
|
66
|
+
def tech(self, domain: str) -> TechResponse:
|
|
67
|
+
return self._t.get(f"/v1/tech/{_enc(domain)}")
|
|
68
|
+
|
|
69
|
+
def threat(self, domain: str) -> ThreatResponse:
|
|
70
|
+
return self._t.get(f"/v1/threat/{_enc(domain)}")
|
|
71
|
+
|
|
72
|
+
def monitor(self, domain: str) -> dict[str, Any]:
|
|
73
|
+
return self._t.get(f"/v1/monitor/{_enc(domain)}")
|
|
74
|
+
|
|
75
|
+
def vulns(self, domain: str) -> dict[str, Any]:
|
|
76
|
+
return self._t.get(f"/v1/domain/{_enc(domain)}/vulns")
|
|
77
|
+
|
|
78
|
+
def audit(self, domain: str) -> AuditResponse:
|
|
79
|
+
return self._t.get(f"/v1/audit/{_enc(domain)}")
|
|
80
|
+
|
|
81
|
+
def wayback(self, domain: str) -> WaybackResponse:
|
|
82
|
+
return self._t.get(f"/v1/archive/{_enc(domain)}")
|
|
83
|
+
|
|
84
|
+
def bulk(self, domains: list[str]) -> BulkDomainResponse:
|
|
85
|
+
return self._t.post("/v1/domains/bulk", json_body={"domains": _validate_domains(domains)})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AsyncDomain:
|
|
89
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
90
|
+
self._t = transport
|
|
91
|
+
|
|
92
|
+
async def report(self, domain: str, *, lite: bool = False) -> DomainReportResponse:
|
|
93
|
+
return await self._t.aget(f"/v1/domain/{_enc(domain)}", params=_report_params(lite))
|
|
94
|
+
|
|
95
|
+
async def dns(self, domain: str) -> DnsResponse:
|
|
96
|
+
return await self._t.aget(f"/v1/dns/{_enc(domain)}")
|
|
97
|
+
|
|
98
|
+
async def whois(self, domain: str) -> WhoisResponse:
|
|
99
|
+
return await self._t.aget(f"/v1/whois/{_enc(domain)}")
|
|
100
|
+
|
|
101
|
+
async def subdomains(self, domain: str) -> SubdomainsResponse:
|
|
102
|
+
return await self._t.aget(f"/v1/subdomains/{_enc(domain)}")
|
|
103
|
+
|
|
104
|
+
async def certs(self, domain: str) -> CertsResponse:
|
|
105
|
+
return await self._t.aget(f"/v1/certs/{_enc(domain)}")
|
|
106
|
+
|
|
107
|
+
async def ssl(self, domain: str) -> SslResponse:
|
|
108
|
+
return await self._t.aget(f"/v1/ssl/{_enc(domain)}")
|
|
109
|
+
|
|
110
|
+
async def tech(self, domain: str) -> TechResponse:
|
|
111
|
+
return await self._t.aget(f"/v1/tech/{_enc(domain)}")
|
|
112
|
+
|
|
113
|
+
async def threat(self, domain: str) -> ThreatResponse:
|
|
114
|
+
return await self._t.aget(f"/v1/threat/{_enc(domain)}")
|
|
115
|
+
|
|
116
|
+
async def monitor(self, domain: str) -> dict[str, Any]:
|
|
117
|
+
return await self._t.aget(f"/v1/monitor/{_enc(domain)}")
|
|
118
|
+
|
|
119
|
+
async def vulns(self, domain: str) -> dict[str, Any]:
|
|
120
|
+
return await self._t.aget(f"/v1/domain/{_enc(domain)}/vulns")
|
|
121
|
+
|
|
122
|
+
async def audit(self, domain: str) -> AuditResponse:
|
|
123
|
+
return await self._t.aget(f"/v1/audit/{_enc(domain)}")
|
|
124
|
+
|
|
125
|
+
async def wayback(self, domain: str) -> WaybackResponse:
|
|
126
|
+
return await self._t.aget(f"/v1/archive/{_enc(domain)}")
|
|
127
|
+
|
|
128
|
+
async def bulk(self, domains: list[str]) -> BulkDomainResponse:
|
|
129
|
+
return await self._t.apost("/v1/domains/bulk", json_body={"domains": _validate_domains(domains)})
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Compact namespaces for endpoints with 1-2 methods each:
|
|
2
|
+
ip, asn, email, phone, password, username.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from urllib.parse import quote
|
|
8
|
+
|
|
9
|
+
from .._transport import _AsyncTransport, _SyncTransport
|
|
10
|
+
from ..models import (
|
|
11
|
+
AsnResponse,
|
|
12
|
+
DisposableResponse,
|
|
13
|
+
EmailMxResponse,
|
|
14
|
+
IpLookupResponse,
|
|
15
|
+
PasswordResponse,
|
|
16
|
+
PhoneLookupResponse,
|
|
17
|
+
ThreatReportResponse,
|
|
18
|
+
UsernameLookupResponse,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _enc(value: str) -> str:
|
|
23
|
+
if value is None or value == "":
|
|
24
|
+
raise ValueError("Missing required parameter")
|
|
25
|
+
return quote(str(value), safe="")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# --- IP ---
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Ip:
|
|
32
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
33
|
+
self._t = transport
|
|
34
|
+
|
|
35
|
+
def lookup(self, ip: str) -> IpLookupResponse:
|
|
36
|
+
return self._t.get(f"/v1/ip/{_enc(ip)}")
|
|
37
|
+
|
|
38
|
+
def threat_report(self, ip: str) -> ThreatReportResponse:
|
|
39
|
+
return self._t.get(f"/v1/threat-report/{_enc(ip)}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AsyncIp:
|
|
43
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
44
|
+
self._t = transport
|
|
45
|
+
|
|
46
|
+
async def lookup(self, ip: str) -> IpLookupResponse:
|
|
47
|
+
return await self._t.aget(f"/v1/ip/{_enc(ip)}")
|
|
48
|
+
|
|
49
|
+
async def threat_report(self, ip: str) -> ThreatReportResponse:
|
|
50
|
+
return await self._t.aget(f"/v1/threat-report/{_enc(ip)}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# --- ASN ---
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Asn:
|
|
57
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
58
|
+
self._t = transport
|
|
59
|
+
|
|
60
|
+
def lookup(self, target: str) -> AsnResponse:
|
|
61
|
+
return self._t.get(f"/v1/asn/{_enc(target)}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AsyncAsn:
|
|
65
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
66
|
+
self._t = transport
|
|
67
|
+
|
|
68
|
+
async def lookup(self, target: str) -> AsnResponse:
|
|
69
|
+
return await self._t.aget(f"/v1/asn/{_enc(target)}")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# --- Email ---
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Email:
|
|
76
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
77
|
+
self._t = transport
|
|
78
|
+
|
|
79
|
+
def mx(self, domain: str) -> EmailMxResponse:
|
|
80
|
+
return self._t.get(f"/v1/email/mx/{_enc(domain)}")
|
|
81
|
+
|
|
82
|
+
def disposable(self, email: str) -> DisposableResponse:
|
|
83
|
+
return self._t.get(f"/v1/email/disposable/{_enc(email)}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class AsyncEmail:
|
|
87
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
88
|
+
self._t = transport
|
|
89
|
+
|
|
90
|
+
async def mx(self, domain: str) -> EmailMxResponse:
|
|
91
|
+
return await self._t.aget(f"/v1/email/mx/{_enc(domain)}")
|
|
92
|
+
|
|
93
|
+
async def disposable(self, email: str) -> DisposableResponse:
|
|
94
|
+
return await self._t.aget(f"/v1/email/disposable/{_enc(email)}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# --- Phone ---
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class Phone:
|
|
101
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
102
|
+
self._t = transport
|
|
103
|
+
|
|
104
|
+
def lookup(self, number: str) -> PhoneLookupResponse:
|
|
105
|
+
return self._t.get(f"/v1/phone/{_enc(number)}")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class AsyncPhone:
|
|
109
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
110
|
+
self._t = transport
|
|
111
|
+
|
|
112
|
+
async def lookup(self, number: str) -> PhoneLookupResponse:
|
|
113
|
+
return await self._t.aget(f"/v1/phone/{_enc(number)}")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# --- Password ---
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class Password:
|
|
120
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
121
|
+
self._t = transport
|
|
122
|
+
|
|
123
|
+
def check(self, sha1_hash: str) -> PasswordResponse:
|
|
124
|
+
return self._t.get(f"/v1/password/{_enc(sha1_hash)}")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class AsyncPassword:
|
|
128
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
129
|
+
self._t = transport
|
|
130
|
+
|
|
131
|
+
async def check(self, sha1_hash: str) -> PasswordResponse:
|
|
132
|
+
return await self._t.aget(f"/v1/password/{_enc(sha1_hash)}")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# --- Username (NEW vs Node SDK v1.3.0) ---
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class Username:
|
|
139
|
+
def __init__(self, transport: _SyncTransport) -> None:
|
|
140
|
+
self._t = transport
|
|
141
|
+
|
|
142
|
+
def lookup(self, username: str) -> UsernameLookupResponse:
|
|
143
|
+
return self._t.get(f"/v1/username/{_enc(username)}")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class AsyncUsername:
|
|
147
|
+
def __init__(self, transport: _AsyncTransport) -> None:
|
|
148
|
+
self._t = transport
|
|
149
|
+
|
|
150
|
+
async def lookup(self, username: str) -> UsernameLookupResponse:
|
|
151
|
+
return await self._t.aget(f"/v1/username/{_enc(username)}")
|