Skip to content

Commit ee65372

Browse files
Port internetnl bit → nibble (#4026)
1 parent bdd2795 commit ee65372

22 files changed

+539
-142
lines changed

octopoes/bits/https_availability/bit.py

-15
This file was deleted.

octopoes/bits/https_availability/https_availability.py

-21
This file was deleted.

octopoes/bits/internetnl/bit.py

-24
This file was deleted.

octopoes/bits/internetnl/internetnl.py

-50
This file was deleted.

octopoes/nibbles/definitions.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from collections.abc import Callable, Iterable
55
from pathlib import Path
66
from types import MethodType, ModuleType
7-
from typing import Any
7+
from typing import Any, get_origin
88

99
import structlog
1010
from pydantic import BaseModel
@@ -19,9 +19,10 @@
1919

2020

2121
class NibbleParameter(BaseModel):
22-
object_type: type[Any]
22+
object_type: Any
2323
parser: str = "[]"
2424
optional: bool = False
25+
additional: set[type[OOI]] = set()
2526

2627
def __eq__(self, other):
2728
if isinstance(other, NibbleParameter):
@@ -31,12 +32,24 @@ def __eq__(self, other):
3132
else:
3233
return False
3334

35+
@property
36+
def triggers(self) -> set[type[OOI]]:
37+
if (
38+
isinstance(self.object_type, type)
39+
and get_origin(self.object_type) is None
40+
and issubclass(self.object_type, OOI)
41+
):
42+
return {self.object_type} | self.additional
43+
else:
44+
return self.additional
45+
3446

3547
class NibbleDefinition(BaseModel):
3648
id: str
3749
signature: list[NibbleParameter]
3850
query: str | Callable[[list[Reference | None]], str] | None = None
3951
enabled: bool = True
52+
additional: set[type[OOI]] = set()
4053
_payload: MethodType | None = None
4154
_checksum: str | None = None
4255

@@ -53,6 +66,10 @@ def __hash__(self):
5366
def _ini(self) -> dict[str, Any]:
5467
return {"id": self.id, "enabled": self.enabled, "checksum": self._checksum}
5568

69+
@property
70+
def triggers(self) -> set[type[OOI]]:
71+
return set.union(*[sgn.triggers for sgn in self.signature]) | self.additional
72+
5673

5774
def get_nibble_definitions() -> dict[str, NibbleDefinition]:
5875
nibble_definitions = {}

octopoes/nibbles/disallowed_csp_hostnames/nibble.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def query(targets: list[Reference | None]) -> str:
100100

101101

102102
NIBBLE = NibbleDefinition(
103-
id="disallowed-csp-hostnames",
103+
id="disallowed_csp_hostnames",
104104
signature=[
105105
NibbleParameter(object_type=HTTPHeaderHostname, parser="[*][?object_type == 'HTTPHeaderHostname'][]"),
106106
NibbleParameter(object_type=Config, parser="[*][?object_type == 'Config'][]", optional=True),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from collections.abc import Iterator
2+
3+
from octopoes.models import OOI
4+
from octopoes.models.ooi.findings import Finding, KATFindingType
5+
from octopoes.models.ooi.network import IPAddress, IPPort
6+
from octopoes.models.ooi.web import Website
7+
8+
9+
def nibble(ipaddress: IPAddress, ipport80: IPPort, website: Website, port443s: int) -> Iterator[OOI]:
10+
_ = ipaddress
11+
_ = ipport80
12+
if port443s < 1:
13+
ft = KATFindingType(id="KAT-HTTPS-NOT-AVAILABLE")
14+
yield ft
15+
yield Finding(
16+
ooi=website.reference,
17+
finding_type=ft.reference,
18+
description="HTTP port is open, but HTTPS port is not open",
19+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from nibbles.definitions import NibbleDefinition, NibbleParameter
2+
from octopoes.models import Reference
3+
from octopoes.models.ooi.network import IPAddress, IPPort
4+
from octopoes.models.ooi.web import Website
5+
6+
7+
def query(targets: list[Reference | None]) -> str:
8+
def pull(statements: list[str]) -> str:
9+
return f"""
10+
{{
11+
:query {{
12+
:find [(pull ?ipaddress [*]) (pull ?ipport80 [*]) (pull ?website [*]) (- (count ?ipport443) 1)]
13+
:where [
14+
{" ".join(statements)}
15+
]
16+
}}
17+
}}
18+
"""
19+
20+
base_query = [
21+
"""
22+
[?website :Website/ip_service ?ip_service]
23+
[?ipservice :IPService/ip_port ?ipport80]
24+
[?ipport80 :IPPort/port 80]
25+
[?ipport80 :IPPort/address ?ipaddress]
26+
(or
27+
(and [?ipport443 :IPPort/address ?ipaddress][?ipport443 :IPPort/port 443])
28+
[(identity nil) ?ipport443]
29+
)
30+
"""
31+
]
32+
33+
ref_queries = [
34+
f'[?ipaddress :IPAddress/primary_key "{str(targets[0])}"]',
35+
f'[?ipport80 :IPPort/primary_key "{str(targets[1])}"]',
36+
f'[?website :Website/primary_key "{str(targets[2])}"]',
37+
]
38+
39+
sgn = "".join(str(int(isinstance(target, Reference))) for target in targets)
40+
if sgn == "1000":
41+
return pull(ref_queries[0:1] + base_query)
42+
elif sgn == "0100":
43+
if int(str(targets[1]).split("|")[-1]) == 80:
44+
return pull(ref_queries[1:2] + base_query)
45+
else:
46+
return pull(base_query)
47+
elif sgn == "0010":
48+
return pull(ref_queries[2:3] + base_query)
49+
elif sgn == "1110":
50+
return pull(ref_queries + base_query)
51+
else:
52+
return pull(base_query)
53+
54+
55+
NIBBLE = NibbleDefinition(
56+
id="https_availability",
57+
signature=[
58+
NibbleParameter(
59+
object_type=IPAddress, parser="[*][?object_type == 'IPAddressV6' || object_type == 'IPAddressV4'][]"
60+
),
61+
NibbleParameter(object_type=IPPort, parser="[*][?object_type == 'IPPort'][]"),
62+
NibbleParameter(object_type=Website, parser="[*][?object_type == 'Website'][]"),
63+
NibbleParameter(object_type=int, parser="[*][-1][]"),
64+
],
65+
query=query,
66+
)
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from collections.abc import Iterator
2+
3+
from octopoes.models import OOI
4+
from octopoes.models.ooi.dns.zone import Hostname
5+
from octopoes.models.ooi.findings import Finding, KATFindingType
6+
7+
8+
def nibble(hostname: Hostname, findings: list[Finding]) -> Iterator[OOI]:
9+
result = "\n".join([str(finding.description) for finding in findings])
10+
11+
if result:
12+
ft = KATFindingType(id="KAT-INTERNETNL")
13+
yield ft
14+
yield Finding(
15+
finding_type=ft.reference,
16+
ooi=hostname.reference,
17+
description=f"This hostname has at least one website with the following finding(s): {result}",
18+
)

octopoes/nibbles/internetnl/nibble.py

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from nibbles.definitions import NibbleDefinition, NibbleParameter
2+
from octopoes.models import Reference
3+
from octopoes.models.ooi.dns.zone import Hostname, Network
4+
from octopoes.models.ooi.findings import Finding
5+
6+
finding_types = [
7+
"KAT-WEBSERVER-NO-IPV6",
8+
"KAT-NAMESERVER-NO-TWO-IPV6",
9+
"KAT-NO-DNSSEC",
10+
"KAT-INVALID-DNSSEC",
11+
"KAT-NO-HSTS",
12+
"KAT-NO-CSP",
13+
"KAT-NO-X-FRAME-OPTIONS",
14+
"KAT-NO-X-CONTENT-TYPE-OPTIONS",
15+
"KAT-CSP-VULNERABILITIES",
16+
"KAT-HSTS-VULNERABILITIES",
17+
"KAT-NO-CERTIFICATE",
18+
"KAT-HTTPS-NOT-AVAILABLE",
19+
"KAT-SSL-CERT-HOSTNAME-MISMATCH",
20+
"KAT-HTTPS-REDIRECT",
21+
]
22+
23+
24+
def or_finding_types() -> str:
25+
clauses = "".join([f'[?finding :Finding/finding_type "{ft}"]' for ft in finding_types])
26+
return f"(or {clauses})"
27+
28+
29+
def query(targets: list[Reference | None]) -> str:
30+
def pull(statements: list[str]) -> str:
31+
return f"""
32+
{{
33+
:query {{
34+
:find [
35+
(pull ?hostname [*])
36+
(pull ?website [*])
37+
(pull ?finding [*])
38+
]
39+
:where [
40+
{" ".join(statements)}
41+
]
42+
}}
43+
}}
44+
"""
45+
46+
base_query = [
47+
"""
48+
[?hostname :object_type "Hostname"]
49+
[?website :Website/hostname ?hostname]
50+
(or-join [?finding ?hostname ?website]
51+
[?finding :Finding/ooi ?hostname]
52+
(and
53+
[?hostnamehttpurl :HostnameHTTPURL/netloc ?hostname]
54+
[?finding :Finding/ooi ?hostnamehttpurl]
55+
)
56+
[?finding :Finding/ooi ?website]
57+
(and
58+
[?resource :HTTPResource/website ?website]
59+
[?finding :Finding/ooi ?resource]
60+
)
61+
(and
62+
[?header :HTTPHeader/resource ?resource]
63+
[?resource :HTTPResource/website ?website]
64+
[?finding :Finding/ooi ?header]
65+
)
66+
)
67+
"""
68+
]
69+
70+
null_query = '{:query {:find [(pull ?var [])] :where [[?var :object_type ""]]}}'
71+
sgn = "".join(str(int(isinstance(target, Reference))) for target in targets)
72+
ref_query = ["[?hostname :Hostname/primary_key]"]
73+
if sgn.startswith("1"):
74+
ref_query = [f'[?hostname :Hostname/primary_key "{str(targets[0])}"]']
75+
elif sgn.endswith("1"):
76+
ref = str(targets[1]).split("|")
77+
if ref[-1] == "KAT-INTERNETNL":
78+
return null_query
79+
else:
80+
tokens = ref[1:-1]
81+
target_reference = Reference.from_str("|".join(tokens))
82+
if tokens[0] == "Hostname":
83+
hostname = target_reference.tokenized
84+
elif tokens[0] in {"HostnameHTTPURL", "Website"}:
85+
hostname = target_reference.tokenized.hostname
86+
elif tokens[0] == "HTTPResource":
87+
hostname = target_reference.tokenized.website.hostname
88+
elif tokens[0] == "HTTPHeader":
89+
hostname = target_reference.tokenized.resource.website.hostname
90+
else:
91+
return null_query
92+
hostname_pk = Hostname(name=hostname.name, network=Network(name=hostname.network.name).reference).reference
93+
ref_query = [f'[?hostname :Hostname/primary_key "{str(hostname_pk)}"]']
94+
return pull(ref_query + base_query)
95+
96+
97+
NIBBLE = NibbleDefinition(
98+
id="internet_nl",
99+
signature=[
100+
NibbleParameter(object_type=Hostname, parser="[*][?object_type == 'Hostname'][]"),
101+
NibbleParameter(object_type=list[Finding], parser="[[*][?object_type == 'Finding'][]]", additional={Finding}),
102+
],
103+
query=query,
104+
)

0 commit comments

Comments
 (0)