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

Add remaining alts to datamodel (SYN-8342) #4064

Merged
merged 4 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changes/4a4fe1c2eb09b8a712af72dc6ce41c38.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
desc: 'Added ``alts`` definitions to the following forms: ``geo:place``, ``it:prod:soft``,
``it:prod:softver``, ``ou:campaign``, ``ou:conference``, ``ou:goal``, ``ou:industry``,
``pol:country``, ``ps:contact``, ``ps:person``, ``risk:threat``, ``risk:tool:software``,
and ``risk:vuln``.'
prs: []
type: model
...
1 change: 1 addition & 0 deletions synapse/models/geopol.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def getModelDefs(self):
('tld', ('inet:fqdn', {}), {}),

('name', ('geo:name', {}), {
'alts': ('names',),
'doc': 'The name of the country.'}),

('names', ('array', {'type': 'geo:name', 'uniq': True, 'sorted': True}), {
Expand Down
1 change: 1 addition & 0 deletions synapse/models/geospace.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ def getModelDefs(self):
('geo:place', {}, (

('name', ('geo:name', {}), {
'alts': ('names',),
'doc': 'The name of the place.'}),

('type', ('geo:place:taxonomy', {}), {
Expand Down
2 changes: 2 additions & 0 deletions synapse/models/infotech.py
Original file line number Diff line number Diff line change
Expand Up @@ -2101,6 +2101,7 @@ def getModelDefs(self):
'doc': 'An ID for the software.'}),

('name', ('it:prod:softname', {}), {
'alts': ('names',),
'doc': 'Name of the software.',
}),
('type', ('it:prod:soft:taxonomy', {}), {
Expand Down Expand Up @@ -2222,6 +2223,7 @@ def getModelDefs(self):
'doc': 'Deprecated. Please use it:prod:softver:name.',
}),
('name', ('it:prod:softname', {}), {
'alts': ('names',),
'doc': 'Name of the software version.',
}),
('names', ('array', {'type': 'it:prod:softname', 'uniq': True, 'sorted': True}), {
Expand Down
5 changes: 5 additions & 0 deletions synapse/models/orgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ def getModelDefs(self):
('ou:goal', {}, (

('name', ('ou:goalname', {}), {
'alts': ('names',),
'doc': 'A terse name for the goal.'}),

('names', ('array', {'type': 'ou:goalname', 'sorted': True, 'uniq': True}), {
Expand Down Expand Up @@ -570,6 +571,7 @@ def getModelDefs(self):
'doc': 'The FQDN of the org responsible for the campaign. Used for entity resolution.'}),

('goal', ('ou:goal', {}), {
'alts': ('goals',),
'doc': 'The assessed primary goal of the campaign.'}),

('slogan', ('lang:phrase', {}), {
Expand All @@ -585,6 +587,7 @@ def getModelDefs(self):
'doc': 'Records the success/failure status of the campaign if known.'}),

('name', ('ou:campname', {}), {
'alts': ('names',),
'ex': 'operation overlord',
'doc': 'A terse name of the campaign.'}),

Expand Down Expand Up @@ -924,6 +927,7 @@ def getModelDefs(self):
('ou:industry', {}, (

('name', ('ou:industryname', {}), {
'alts': ('names',),
'doc': 'The name of the industry.'}),

('type', ('ou:industry:type:taxonomy', {}), {
Expand Down Expand Up @@ -1176,6 +1180,7 @@ def getModelDefs(self):
'doc': 'An array of contacts which sponsored the conference.',
}),
('name', ('entity:name', {}), {
'alts': ('names',),
'doc': 'The full name of the conference.',
'ex': 'defcon 2017'}),

Expand Down
8 changes: 8 additions & 0 deletions synapse/models/person.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ def getModelDefs(self):
'doc': 'The most recent known vitals for the person.',
}),
('name', ('ps:name', {}), {
'alts': ('names',),
'doc': 'The localized name for the person.',
}),
('name:sur', ('ps:tokn', {}), {
Expand Down Expand Up @@ -350,6 +351,7 @@ def getModelDefs(self):
'doc': 'The most recent known vitals for the contact.',
}),
('name', ('ps:name', {}), {
'alts': ('names',),
'doc': 'The person name listed for the contact.'}),

('bio', ('str', {}), {
Expand All @@ -359,6 +361,7 @@ def getModelDefs(self):
'doc': 'A description of this contact.'}),

('title', ('ou:jobtitle', {}), {
'alts': ('titles',),
'doc': 'The job/org title listed for this contact.'}),

('titles', ('array', {'type': 'ou:jobtitle', 'sorted': True, 'uniq': True}), {
Expand All @@ -368,12 +371,14 @@ def getModelDefs(self):
'doc': 'The photo listed for this contact.',
}),
('orgname', ('ou:name', {}), {
'alts': ('orgnames',),
'doc': 'The listed org/company name for this contact.',
}),
('orgfqdn', ('inet:fqdn', {}), {
'doc': 'The listed org/company FQDN for this contact.',
}),
('user', ('inet:user', {}), {
'alts': ('users',),
'doc': 'The username or handle for this contact.'}),

('service:accounts', ('array', {'type': 'inet:service:account', 'sorted': True, 'uniq': True}), {
Expand Down Expand Up @@ -415,6 +420,7 @@ def getModelDefs(self):
'doc': 'The home or main site for this contact.',
}),
('email', ('inet:email', {}), {
'alts': ('emails',),
'doc': 'The main email address for this contact.',
}),
('email:work', ('inet:email', {}), {
Expand Down Expand Up @@ -443,6 +449,7 @@ def getModelDefs(self):
'doc': 'The work phone number for this contact.',
}),
('id:number', ('ou:id:number', {}), {
'alts': ('id:numbers',),
'doc': 'An ID number issued by an org and associated with this contact.',
}),
('adid', ('it:adid', {}), {
Expand Down Expand Up @@ -482,6 +489,7 @@ def getModelDefs(self):
}),

('lang', ('lang:language', {}), {
'alts': ('langs',),
'doc': 'The language specified for the contact.'}),

('langs', ('array', {'type': 'lang:language'}), {
Expand Down
3 changes: 3 additions & 0 deletions synapse/models/risk.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ def getModelDefs(self):
'doc': "The reporting organization's assessed location of the threat cluster."}),

('org:name', ('ou:name', {}), {
'alts': ('org:names',),
'ex': 'apt1',
'doc': "The reporting organization's name for the threat cluster."}),

Expand Down Expand Up @@ -383,6 +384,7 @@ def getModelDefs(self):
'doc': 'The authoritative software family for the tool.'}),

('soft:name', ('it:prod:softname', {}), {
'alts': ('soft:names',),
'doc': 'The reporting organization\'s name for the tool.'}),

('soft:names', ('array', {'type': 'it:prod:softname', 'uniq': True, 'sorted': True}), {
Expand Down Expand Up @@ -441,6 +443,7 @@ def getModelDefs(self):
('risk:vuln', {}, (

('name', ('risk:vulnname', {}), {
'alts': ('names',),
'doc': 'A user specified name for the vulnerability.'}),

('names', ('array', {'type': 'risk:vulnname', 'sorted': True, 'uniq': True}), {
Expand Down
4 changes: 4 additions & 0 deletions synapse/tests/test_model_geopol.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ async def test_geopol_country(self):
]
''')
self.len(1, nodes)
node = nodes[0]
self.eq('visiland', nodes[0].get('name'))
self.eq(('visitopia',), nodes[0].get('names'))
self.eq(1640995200000, nodes[0].get('founded'))
Expand All @@ -29,6 +30,9 @@ async def test_geopol_country(self):
self.len(2, await core.nodes('pol:country -> geo:name'))
self.len(3, await core.nodes('pol:country -> econ:currency'))

self.len(1, nodes := await core.nodes('[ pol:country=({"name": "visitopia"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

nodes = await core.nodes('''
[ pol:vitals=*
:country={pol:country:name=visiland}
Expand Down
6 changes: 6 additions & 0 deletions synapse/tests/test_model_geospace.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ async def test_types_forms(self):
nodes = await core.nodes('[ geo:place=(hehe, haha) :names=("Foo Bar ", baz) ] -> geo:name')
self.eq(('baz', 'foo bar'), [n.ndef[1] for n in nodes])

nodes = await core.nodes('geo:place=(hehe, haha)')
node = nodes[0]

self.len(1, nodes := await core.nodes('[ geo:place=({"name": "baz"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

async def test_eq(self):

async with self.getTestCore() as core:
Expand Down
8 changes: 8 additions & 0 deletions synapse/tests/test_model_infotech.py
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,10 @@ async def test_it_forms_prodsoft(self):
self.eq(node.get('url'), url0)
self.len(1, await core.nodes('it:prod:soft:name="balloon maker" -> it:prod:soft:taxonomy'))
self.len(2, await core.nodes('it:prod:softname="balloon maker" -> it:prod:soft -> it:prod:softname'))

self.len(1, nodes := await core.nodes('[ it:prod:soft=({"name": "clowns inc"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

# it:prod:softver - this does test a bunch of property related callbacks
ver0 = s_common.guid()
url1 = 'https://vertex.link/products/balloonmaker/release_101-beta.exe'
Expand Down Expand Up @@ -820,6 +824,10 @@ async def test_it_forms_prodsoft(self):
self.eq(node.get('url'), url1)
self.eq(node.get('name'), 'balloonmaker')
self.eq(node.get('desc'), 'makes balloons')

self.len(1, nodes := await core.nodes('[ it:prod:softver=({"name": "clowns inc"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

# callback node creation checks
self.len(1, await core.nodes('it:dev:str=V1.0.1-beta+exp.sha.5114f85'))
self.len(1, await core.nodes('it:dev:str=amd64'))
Expand Down
19 changes: 17 additions & 2 deletions synapse/tests/test_model_orgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ async def test_ou_simple(self):
self.eq(node.get('desc'), 'MyDesc')
self.eq(node.get('prev'), goal)

self.len(1, nodes := await core.nodes('[ ou:goal=({"name": "foo goal"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

nodes = await core.nodes('[(ou:hasgoal=$valu :stated=$lib.true :window="2019,2020")]',
opts={'vars': {'valu': (org0, goal)}})
self.len(1, nodes)
Expand All @@ -69,12 +72,13 @@ async def test_ou_simple(self):
self.eq(node.get('stated'), True)
self.eq(node.get('window'), (1546300800000, 1577836800000))

altgoal = s_common.guid()
timeline = s_common.guid()

props = {
'org': org0,
'goal': goal,
'goals': (goal,),
'goals': (goal, altgoal),
'actors': (acto,),
'camptype': 'get.pizza',
'name': 'MyName',
Expand Down Expand Up @@ -103,7 +107,7 @@ async def test_ou_simple(self):
self.eq(node.get('tag'), 'cno.camp.31337')
self.eq(node.get('org'), org0)
self.eq(node.get('goal'), goal)
self.eq(node.get('goals'), (goal,))
self.eq(node.get('goals'), sorted((goal, altgoal)))
self.eq(node.get('actors'), (acto,))
self.eq(node.get('name'), 'myname')
self.eq(node.get('names'), ('bar', 'foo'))
Expand All @@ -120,6 +124,10 @@ async def test_ou_simple(self):
self.eq(node.get('mitre:attack:campaign'), 'C0011')
self.eq(node.get('slogan'), 'for the people')

opts = {'vars': {'altgoal': altgoal}}
self.len(1, nodes := await core.nodes('[ ou:campaign=({"name": "foo", "goal": $altgoal}) ]', opts=opts))
self.eq(node.ndef, nodes[0].ndef)

self.len(1, await core.nodes(f'ou:campaign={camp} :slogan -> lang:phrase'))
nodes = await core.nodes(f'ou:campaign={camp} -> it:mitre:attack:campaign')
self.len(1, nodes)
Expand Down Expand Up @@ -405,6 +413,9 @@ async def test_ou_simple(self):
self.eq(node.get('place'), place0)
self.eq(node.get('url'), 'http://arrowcon.org/2018')

self.len(1, nodes := await core.nodes('[ ou:conference=({"name": "arrcon18"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

props = {
'arrived': '201803010800',
'departed': '201803021500',
Expand Down Expand Up @@ -870,6 +881,7 @@ async def test_ou_industry(self):
] '''
nodes = await core.nodes(q)
self.len(1, nodes)
node = nodes[0]
self.nn(nodes[0].get('reporter'))
self.eq('foo bar', nodes[0].get('name'))
self.eq('vertex', nodes[0].get('reporter:name'))
Expand All @@ -884,6 +896,9 @@ async def test_ou_industry(self):
self.len(3, nodes)
self.len(3, await core.nodes('ou:industryname=baz -> ou:industry -> ou:industryname'))

self.len(1, nodes := await core.nodes('[ ou:industry=({"name": "faz"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

async def test_ou_opening(self):

async with self.getTestCore() as core:
Expand Down
22 changes: 21 additions & 1 deletion synapse/tests/test_model_person.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ async def test_ps_simple(self):
self.eq(node.get('names'), ['billy bob'])
self.eq(node.get('photo'), file0)

self.len(1, nodes := await core.nodes('[ ps:person=({"name": "billy bob"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

props = {
'dob': '2000',
'img': file0,
Expand Down Expand Up @@ -147,6 +150,7 @@ async def test_ps_simple(self):
'id:numbers': (('*', 'asdf'), ('*', 'qwer')),
'users': ('visi', 'invisigoth'),
'crypto:address': 'btc/1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
'langs': (lang00 := s_common.guid(),),
}
opts = {'vars': {'valu': con0, 'p': props}}
q = '''[(ps:contact=$valu
Expand All @@ -166,7 +170,7 @@ async def test_ps_simple(self):
:birth:place:name=$p."birth:place:name"
:death:place=$p."death:place" :death:place:loc=$p."death:place:loc"
:death:place:name=$p."death:place:name"
:service:accounts=(*, *)
:service:accounts=(*, *) :langs=$p.langs
)]'''
nodes = await core.nodes(q, opts=opts)
self.len(1, nodes)
Expand Down Expand Up @@ -213,6 +217,22 @@ async def test_ps_simple(self):
self.len(1, await core.nodes('ps:contact :death:place -> geo:place'))
self.len(2, await core.nodes('ps:contact :service:accounts -> inet:service:account'))

opts = {
'vars': {
'ctor': {
'email': '[email protected]',
'id:number': node.get('id:numbers')[0],
'lang': lang00,
'name': 'vi',
'orgname': 'vertex',
'title': 'haha',
'user': 'invisigoth',
},
},
}
self.len(1, nodes := await core.nodes('[ ps:contact=$ctor ]', opts=opts))
self.eq(node.ndef, nodes[0].ndef)

nodes = await core.nodes('''[
ps:achievement=*
:award=*
Expand Down
11 changes: 11 additions & 0 deletions synapse/tests/test_model_risk.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ async def addNode(text):
self.len(1, await core.nodes('risk:attack :target -> ps:contact'))
self.len(1, await core.nodes('risk:attack :attacker -> ps:contact'))

self.len(1, nodes := await core.nodes('[ risk:vuln=({"name": "hehe"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

node = await addNode(f'''[
risk:hasvuln={hasv}
:vuln={vuln}
Expand Down Expand Up @@ -399,6 +402,7 @@ async def addNode(text):
]
''')
self.len(1, nodes)
node = nodes[0]
self.eq('vtx-apt1', nodes[0].get('name'))
self.eq('VTX-APT1', nodes[0].get('desc'))
self.eq(40, nodes[0].get('activity'))
Expand All @@ -424,6 +428,9 @@ async def addNode(text):
self.len(1, await core.nodes('risk:threat:merged:isnow -> risk:threat'))
self.len(1, await core.nodes('risk:threat -> it:mitre:attack:group'))

self.len(1, nodes := await core.nodes('[ risk:threat=({"org:name": "comment crew"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

nodes = await core.nodes('''[ risk:leak=*
:name="WikiLeaks ACME Leak"
:desc="WikiLeaks leaked ACME stuff."
Expand Down Expand Up @@ -618,6 +625,7 @@ async def test_model_risk_tool_software(self):
]
''')
self.len(1, nodes)
node = nodes[0]
self.nn(nodes[0].get('soft'))

self.nn(nodes[0].get('reporter'))
Expand All @@ -640,6 +648,9 @@ async def test_model_risk_tool_software(self):
self.len(1, await core.nodes('risk:tool:software -> syn:tag'))
self.len(1, await core.nodes('risk:tool:software -> it:mitre:attack:software'))

self.len(1, nodes := await core.nodes('[ risk:tool:software=({"soft:name": "beacon"}) ]'))
self.eq(node.ndef, nodes[0].ndef)

nodes = await core.nodes('''
[ risk:vuln:soft:range=*
:vuln={[ risk:vuln=* :name=woot ]}
Expand Down
Loading