Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

risk:outage meta:aggregate and reporter props on ou:industry #3968

Merged
merged 12 commits into from
Nov 4, 2024
6 changes: 6 additions & 0 deletions changes/1e9a7d3fe713a7f640de326e1db60a5e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
desc: Added ``:reporter`` and ``:reporter:name`` to the ``ou:industry`` form to allow
reporter specific industries.
prs: []
type: model
...
5 changes: 5 additions & 0 deletions changes/7cf52c63ad724ae8a22dc1d077c6587f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
desc: Added ``risk:outage`` to represent outage events.
prs: []
type: model
...
5 changes: 5 additions & 0 deletions changes/baa0bf5683fdaf277adb91c5c326913b.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
desc: Added ``meta:aggregate`` to represent aggregate counts.
prs: []
type: model
...
27 changes: 27 additions & 0 deletions synapse/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,19 @@ def getModelDefs(self):

('meta:sophistication', ('int', {'enums': sophenums}), {
'doc': 'A sophistication score with named values: very low, low, medium, high, and very high.'}),

('meta:aggregate:type:taxonomy', ('taxonomy', {}), {
'interfaces': ('meta:taxonomy',),
'doc': 'A type of item being counted in aggregate.'}),

('meta:aggregate', ('guid', {}), {
'display': {
'columns': (
{'type': 'prop', 'opts': {'name': 'type'}},
{'type': 'prop', 'opts': {'name': 'count'}},
),
},
'doc': 'A node which represents an aggregate count of a specific type.'}),
),
'interfaces': (
('meta:taxonomy', {
Expand Down Expand Up @@ -285,6 +298,20 @@ def getModelDefs(self):
'doc': 'An external identifier for the rule.'}),
)),

('meta:aggregate:type:taxonomy', {}, ()),
('meta:aggregate', {}, (
invisig0th marked this conversation as resolved.
Show resolved Hide resolved

('type', ('meta:aggregate:type:taxonomy', {}), {
'ex': 'casualties.civilian',
'doc': 'The type of items being counted in aggregate.'}),

('time', ('time', {}), {
'doc': 'The time that the count was computed.'}),

('count', ('int', {}), {
'doc': 'The number of items counted in aggregate.'}),
)),

('graph:cluster', {}, (
('name', ('str', {'lower': True}), {
'doc': 'A human friendly name for the cluster.'}),
Expand Down
6 changes: 6 additions & 0 deletions synapse/models/orgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,12 @@ def getModelDefs(self):
('names', ('array', {'type': 'ou:industryname', 'uniq': True, 'sorted': True}), {
'doc': 'An array of alternative names for the industry.'}),

('reporter', ('ou:org', {}), {
'doc': 'The organization reporting on the industry.'}),

('reporter:name', ('ou:name', {}), {
'doc': 'The name of the organization reporting on the industry.'}),

('subs', ('array', {'type': 'ou:industry', 'split': ',', 'uniq': True, 'sorted': True}), {
'deprecated': True,
'doc': 'Deprecated. Please use ou:industry:type taxonomy.'}),
Expand Down
66 changes: 66 additions & 0 deletions synapse/models/risk.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,27 @@ def getModelDefs(self):
('risk:extortion', ('guid', {}), {
'doc': 'An event where an attacker attempted to extort a victim.'}),

('risk:outage:cause:taxonomy', ('taxonomy', {}), {
'interfaces': ('meta:taxonomy',),
'doc': 'An outage cause taxonomy.'}),

('risk:outage:type:taxonomy', ('taxonomy', {}), {
'interfaces': ('meta:taxonomy',),
'doc': 'An outage type taxonomy.'}),

('risk:outage', ('guid', {}), {
'display': {
'columns': (
{'type': 'prop', 'opts': {'name': 'name'}},
{'type': 'prop', 'opts': {'name': 'type'}},
{'type': 'prop', 'opts': {'name': 'cause'}},
{'type': 'prop', 'opts': {'name': 'period'}},
{'type': 'prop', 'opts': {'name': 'provider:name'}},
{'type': 'prop', 'opts': {'name': 'reporter:name'}},
),
},
'doc': 'An outage event which affected resource availability.'}),

('risk:extortion:type:taxonomy', ('taxonomy', {}), {
'interfaces': ('meta:taxonomy',),
'doc': 'A taxonomy of extortion event types.'}),
Expand Down Expand Up @@ -224,6 +245,15 @@ def getModelDefs(self):

(('risk:extortion', 'leveraged', None), {
'doc': 'The extortion event was based on attacker access to the target node.'}),

(('meta:event', 'caused', 'risk:outage'), {
'doc': 'The event caused the outage.'}),

(('risk:attack', 'caused', 'risk:outage'), {
'doc': 'The attack caused the outage.'}),

(('risk:outage', 'impacted', None), {
'doc': 'The outage event impacted the availability of the target node.'}),
),
'forms': (

Expand Down Expand Up @@ -1025,6 +1055,42 @@ def getModelDefs(self):

)),

('risk:outage:type:taxonomy', {}, ()),
('risk:outage:cause:taxonomy', {}, ()),
('risk:outage', {}, (
invisig0th marked this conversation as resolved.
Show resolved Hide resolved
invisig0th marked this conversation as resolved.
Show resolved Hide resolved

('name', ('str', {'lower': True, 'onespace': True}), {
'doc': 'A name for the outage event.'}),

('period', ('ival', {}), {
'doc': 'The time period where the outage impacted availability.'}),

('type', ('risk:outage:type:taxonomy', {}), {
'ex': 'service.power',
'doc': 'The type of outage.'}),

('cause', ('risk:outage:cause:taxonomy', {}), {
invisig0th marked this conversation as resolved.
Show resolved Hide resolved
invisig0th marked this conversation as resolved.
Show resolved Hide resolved
'ex': 'nature.earthquake',
'doc': 'The outage cause type.'}),

('provider', ('ou:org', {}), {
'doc': 'The organization which experienced the outage event.'}),

('provider:name', ('ou:name', {}), {
'doc': 'The name of the organization which experienced the outage event.'}),

('reporter:name', ('ou:name', {}), {
'doc': 'The name of the organization reporting on the outage event.'}),

('reporter', ('ou:org', {}), {
'doc': 'The organization reporting on the outage event.'}),

('reporter:name', ('ou:name', {}), {
'doc': 'The name of the organization reporting on the outage event.'}),
Comment on lines +1088 to +1089
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate reporter:name prop

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll put up another PR.

)),

# TODO risk:outage:vitals to track outage stats over time

('risk:extortion:type:taxonomy', {}, ()),
('risk:extortion', {}, (

Expand Down
11 changes: 11 additions & 0 deletions synapse/tests/test_model_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,14 @@ async def test_model_doc_deprecated(self):
for node in nodes:
form = core.model.form(node.ndef[1])
self.true(form.deprecated, msg=form)

async def test_model_aggregate(self):

async with self.getTestCore() as core:

nodes = await core.nodes('[ meta:aggregate=* :count=99 :type=bottles :time=20240202 ]')
self.len(1, nodes)
self.eq(99, nodes[0].get('count'))
self.eq('bottles.', nodes[0].get('type'))
self.eq(1706832000000, nodes[0].get('time'))
self.len(1, await core.nodes('meta:aggregate -> meta:aggregate:type:taxonomy'))
5 changes: 5 additions & 0 deletions synapse/tests/test_model_orgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,16 +865,21 @@ async def test_ou_industry(self):
:sic="1234,5678"
:isic=C1393
:desc="Moldy cheese"
:reporter={[ ou:org=* :name=vertex ]}
:reporter:name=vertex
] '''
nodes = await core.nodes(q)
self.len(1, nodes)
self.nn(nodes[0].get('reporter'))
self.eq('foo bar', nodes[0].get('name'))
self.eq('vertex', nodes[0].get('reporter:name'))
self.sorteq(('1234', '5678'), nodes[0].get('sic'))
self.sorteq(('11111', '22222'), nodes[0].get('naics'))
self.sorteq(('C1393', ), nodes[0].get('isic'))
self.len(2, nodes[0].get('subs'))
self.eq('Moldy cheese', nodes[0].get('desc'))

self.len(1, await core.nodes('ou:industry :reporter -> ou:org'))
nodes = await core.nodes('ou:industry:name="foo bar" | tree { :subs -> ou:industry } | uniq')
self.len(3, nodes)
self.len(3, await core.nodes('ou:industryname=baz -> ou:industry -> ou:industryname'))
Expand Down
25 changes: 25 additions & 0 deletions synapse/tests/test_model_risk.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,31 @@ async def addNode(text):
self.len(1, await core.nodes('risk:vuln:name=redtree -> risk:vulnerable :node -> *'))
self.len(1, await core.nodes('risk:vulnerable -> risk:mitigation'))

nodes = await core.nodes('''
[ risk:outage=*
:name="The Big One"
:period=(2023, 2024)
:type=service.power
:cause=nature.earthquake
:provider={[ ou:org=* :name="desert power" ]}
:provider:name="desert power"
:reporter={ ou:org:name=vertex }
:reporter:name=vertex
]
''')
self.len(1, nodes)
self.nn(nodes[0].get('reporter'))
self.eq('the big one', nodes[0].get('name'))
self.eq('vertex', nodes[0].get('reporter:name'))
self.eq('desert power', nodes[0].get('provider:name'))
self.eq('service.power.', nodes[0].get('type'))
self.eq('nature.earthquake.', nodes[0].get('cause'))
self.eq((1672531200000, 1704067200000), nodes[0].get('period'))

self.len(1, await core.nodes('risk:outage -> risk:outage:cause:taxonomy'))
self.len(1, await core.nodes('risk:outage :reporter -> ou:org +:name=vertex'))
self.len(1, await core.nodes('risk:outage :provider -> ou:org +:name="desert power"'))

async def test_model_risk_mitigation(self):
async with self.getTestCore() as core:
nodes = await core.nodes('''[
Expand Down
Loading