diff --git a/synapse/common.py b/synapse/common.py index b307a015ec..72ec479bf7 100644 --- a/synapse/common.py +++ b/synapse/common.py @@ -524,7 +524,7 @@ def yamlload(*paths): with io.open(path, 'rb') as fd: return yamlloads(fd) -def yamldump(obj, stream: typing.Optional[typing.BinaryIO] =None) -> bytes: +def yamldump(obj, stream=None): ''' Dump a object to yaml. diff --git a/synapse/datamodel.py b/synapse/datamodel.py index b9ae8c88f3..ef13e2b5a9 100644 --- a/synapse/datamodel.py +++ b/synapse/datamodel.py @@ -973,9 +973,29 @@ def addDataModels(self, mods): ctors[name] = (name, ctor, opts, info) # load all the types in order... + typetodo = {} for _, mdef in mods: - for typename, (basename, typeopts), typeinfo in mdef.get('types', ()): - self.addType(typename, basename, typeopts, typeinfo, skipinit=True) + for typename, basetype, typeopts in mdef.get('types', ()): + typetodo[typename] = (basetype, typeopts) + + def _addType(typename, basetype, typeinfo): + + if self.types.get(typename) is not None: + return + + # see if we can resolve our base type if it's not loaded yet + if self.types.get(basetype[0]) is None: + todo = typetodo.get(basetype[0]) + if todo is None: + mesg = f'No such base type {basetype[0]} for type {typename}.' + raise s_exc.NoSuchType(mesg=mesg) + + _addType(basetype[0], todo[0], todo[1]) + + self.addType(typename, basetype[0], basetype[1], typeinfo, skipinit=True) + + for (typename, (basetype, typeinfo)) in typetodo.items(): + _addType(typename, basetype, typeinfo) # finish initializing types for name, tobj in self.types.items(): @@ -1152,7 +1172,8 @@ def addType(self, typename, basename, typeopts, typeinfo, skipinit=False): base = self.types.get(basename) if base is None: - raise s_exc.NoSuchType(name=basename) + mesg = f'No such base type: {basename} for type {typename}.' + raise s_exc.NoSuchType(mesg=mesg, name=basename) newtype = base.extend(typename, typeopts, typeinfo, skipinit=skipinit) @@ -1229,11 +1250,12 @@ def addForm(self, formname, forminfo, propdefs, checks=True): if prop.ifaces: continue - if (newdef := ptypes.get(prop.name)) is not None: - if newdef != prop.typedef: - mesg = f'Form {formname} overrides inherited prop {prop.name} with a different typedef.' - raise s_exc.BadPropDef(mesg=mesg, typedef=newdef, form=formname, prop=prop.name) - continue + # TODO: this should check for a common base type... + # if (newdef := ptypes.get(prop.name)) is not None: + # if newdef != prop.typedef: + # mesg = f'Form {formname} overrides inherited prop {prop.name} with a different typedef.' + # raise s_exc.BadPropDef(mesg=mesg, typedef=newdef, form=formname, prop=prop.name) + # continue pprops.append((prop.name, prop.typedef, prop.info)) @@ -1390,6 +1412,10 @@ def addIface(self, name, info): self.ifaces[name] = info + # FIXME polyprops + if self.types.get(name) is None: + self.addType(name, 'ndef', {'interface': name}, {'doc': 'FIXME POLYPROP PLACE HOLDER'}) + def reqTypeNotInUse(self, typename): if self.propsbytype.get(typename): mesg = f'Cannot delete type {typename} as it is still in use by properties.' diff --git a/synapse/models/base.py b/synapse/models/base.py index 091514d762..201640f426 100644 --- a/synapse/models/base.py +++ b/synapse/models/base.py @@ -81,12 +81,44 @@ 'doc': 'A hierarchical taxonomy of timeline types.'}), ('meta:event', ('guid', {}), { - 'doc': 'An analytically relevant event in a curated timeline.'}), + 'template': {'title': 'event'}, + 'interfaces': ( + ('base:event', {}), + ), + 'props': ( + ('title', ('str', {}), { + 'doc': 'A title for the {title}.'}), + + ('desc', ('text', {}), { + 'doc': 'A description of the {title}.'}), + + ('type', ('meta:event:type:taxonomy', {}), { + 'doc': 'The type of event.'}), + ), + 'doc': 'An analytically relevant event.'}), + + ('meta:activity', ('guid', {}), { + 'template': {'title': 'activity'}, + 'interfaces': ( + ('base:activity', {}), + ), + 'props': ( + ('name', ('base:name', {}), { + 'doc': 'The name of the {title}.'}), + + ('desc', ('text', {}), { + 'doc': 'A description of the {title}.'}), + + ('type', ('meta:event:type:taxonomy', {}), { + 'doc': 'The type of activity.'}), + ), + 'doc': 'Analytically relevant activity.'}), ('meta:event:type:taxonomy', ('taxonomy', {}), { 'interfaces': ( ('meta:taxonomy', {}), ), + 'props': (), 'doc': 'A hierarchical taxonomy of event types.'}), ('meta:ruleset:type:taxonomy', ('taxonomy', {}), { @@ -133,12 +165,6 @@ }, 'doc': 'A node which represents an aggregate count of a specific type.'}), - ('meta:havable', ('ndef', {'interface': 'meta:havable'}), { - 'doc': 'An item which may be possessed by an entity.'}), - - ('meta:discoverable', ('ndef', {'interface': 'meta:discoverable'}), { - 'doc': 'FIXME polyprop place holder'}), - ('text', ('str', {'strip': False}), { 'doc': 'A multi-line, free form text string.'}), @@ -169,6 +195,37 @@ ('meta:taxonomy', {}), ), 'doc': 'A hierarchical taxonomy of technique types.'}), + + ('meta:award:type:taxonomy', ('taxonomy', {}), { + 'interfaces': ( + ('meta:taxonomy', {}), + ), + 'doc': 'A hierarchical taxonomy of award types.'}), + + ('meta:award', ('guid', {}), { + 'template': {'title': 'award'}, + 'interfaces': ( + ('meta:awardable', {}), + ), + 'props': ( + ('name', ('base:name', {}), { + 'ex': 'nobel peace prize', + 'doc': 'The name of the award.'}), + + ('desc', ('text', {}), { + 'doc': 'A description of the award.'}), + + # TODO: are award types really a thing? + ('type', ('meta:award:type:taxonomy', {}), { + 'doc': 'The type of award.'}), + + ('issuer', ('entity:actor', {}), { + 'doc': 'The entity who issues the award.'}), + + ('period', ('ival', {}), { + 'doc': 'The period of time when the award was being issued.'}), + ), + 'doc': 'An award which can be granted to an actor.'}), ), 'interfaces': ( @@ -181,6 +238,25 @@ ), }), + ('meta:promotable', { + 'props': ( + ('website', ('inet:url', {}), { + 'doc': 'The URL of the {title} website.'}), + + ('social:accounts', ('array', {'type': 'inet:service:account'}), { + 'doc': 'Social media accounts for the {title}.'}), + ), + 'doc': 'Properties common to promoted events or activities.'}), + + ('meta:attendable', { + 'interfaces': ( + ('base:activity', {}), + ), + 'doc': 'An interface implemented by activities which may be attended.'}), + + ('meta:sponsorable', { + 'doc': 'An interface implemented by activities which may be sponsored.'}), + ('meta:havable', { 'doc': 'An interface used to describe items that can be possessed by an entity.', 'template': {'title': 'item'}, @@ -216,21 +292,21 @@ }, 'props': ( - ('id', ('meta:id', {}), { + ('id', ('base:id', {}), { 'alts': ('ids',), 'doc': 'A unique ID given to the {title}.'}), - ('ids', ('array', {'type': 'meta:id'}), { + ('ids', ('array', {'type': 'base:id'}), { 'doc': 'An array of alternate IDs given to the {title}.'}), ('url', ('inet:url', {}), { 'doc': 'The URL for the {title}.'}), - ('name', ('meta:name', {}), { + ('name', ('base:name', {}), { 'alts': ('names',), 'doc': 'The primary name of the {title}.'}), - ('names', ('array', {'type': 'meta:name'}), { + ('names', ('array', {'type': 'base:name'}), { 'doc': 'A list of alternate names for the {title}.'}), ('desc', ('text', {}), { @@ -288,6 +364,37 @@ ), }), + ('meta:causal', { + 'doc': 'Implemented by events and activities which can lead to effects.'}), + + ('base:event', { + 'template': {'title': 'event'}, + 'interfaces': ( + ('meta:causal', {}), + ), + 'props': ( + ('time', ('time', {}), { + 'doc': 'The time that the {title} occurred.'}), + + ('activity', ('meta:activity', {}), { + 'doc': 'A parent activity which includes this {title}.'}), + ), + 'doc': 'Properties common to an event.'}), + + ('base:activity', { + 'template': {'title': 'activity'}, + 'interfaces': ( + ('meta:causal', {}), + ), + 'props': ( + ('period', ('ival', {}), { + 'doc': 'The period over which the {title} occurred.'}), + + ('activity', ('meta:activity', {}), { + 'doc': 'A parent activity which includes this {title}.'}), + ), + 'doc': 'Properties common to activity which occurs over a period.'}), + ('meta:usable', { 'template': {'title': 'item'}, 'props': ( @@ -296,6 +403,15 @@ ), 'doc': 'An interface for forms which can be used by an actor.'}), + ('meta:competitive', { + 'doc': 'An interface implemented by organized competitive activities.'}), + + ('meta:awardable', { + 'doc': 'An interface implemented by forms which may be awarded to an actor.'}), + + ('meta:believable', { + 'doc': 'An interface for forms which may be believed in by an actor.'}), + ('meta:matchish', { 'doc': 'Properties which are common to matches based on rules.', 'template': {'rule': 'rule', 'rule:type': 'rule:type', @@ -367,6 +483,9 @@ (('meta:technique', 'addresses', 'risk:vuln'), { 'doc': 'The technique addresses the vulnerability.'}), + + (('meta:causal', 'ledto', 'meta:causal'), { + 'doc': 'The source event led to the target event.'}), ), 'forms': ( @@ -474,24 +593,6 @@ ('meta:timeline:type:taxonomy', { 'prevnames': ('meta:timeline:taxonomy',)}, ()), - ('meta:event', {}, ( - - ('period', ('ival', {}), { - 'doc': 'The period over which the event occurred.'}), - - ('title', ('str', {}), { - 'doc': 'A title for the event.'}), - - ('desc', ('text', {}), { - 'doc': 'A description of the event.'}), - - ('type', ('meta:event:type:taxonomy', {}), { - 'doc': 'Type of event.'}), - )), - - ('meta:event:type:taxonomy', { - 'prevnames': ('meta:event:taxonomy',)}, ()), - ('meta:ruleset', {}, ( ('name', ('base:id', {}), { diff --git a/synapse/models/belief.py b/synapse/models/belief.py index 0fa17248a4..fbe363f89a 100644 --- a/synapse/models/belief.py +++ b/synapse/models/belief.py @@ -3,6 +3,9 @@ 'types': ( ('belief:system', ('guid', {}), { + 'interfaces': ( + ('meta:believable', {}), + ), 'doc': 'A belief system such as an ideology, philosophy, or religion.'}), ('belief:system:type:taxonomy', ('taxonomy', {}), { @@ -12,10 +15,10 @@ 'doc': 'A hierarchical taxonomy of belief system types.'}), ('belief:tenet', ('guid', {}), { + 'interfaces': ( + ('meta:believable', {}), + ), 'doc': 'A concrete tenet potentially shared by multiple belief systems.'}), - - ('belief:subscriber', ('guid', {}), { - 'doc': 'A contact which subscribes to a belief system.'}), ), 'forms': ( @@ -32,7 +35,6 @@ ('began', ('time', {}), { 'doc': 'The time that the belief system was first observed.'}), - )), ('belief:system:type:taxonomy', {}, ()), @@ -46,26 +48,11 @@ 'doc': 'A description of the tenet.'}), )), - ('belief:subscriber', {}, ( - - ('contact', ('entity:individual', {}), { - 'doc': 'The individual who subscribes to the belief system.'}), - - ('system', ('belief:system', {}), { - 'doc': 'The belief system to which the contact subscribes.'}), - - ('period', ('ival', {}), { - 'prevnames': ('began', 'ended'), - 'doc': 'The time period when the contact subscribed to the belief system.'}), - )), ), 'edges': ( (('belief:system', 'has', 'belief:tenet'), { 'doc': 'The belief system includes the tenet.'}), - - (('belief:subscriber', 'follows', 'belief:tenet'), { - 'doc': 'The subscriber is assessed to generally adhere to the specific tenet.'}), ), }), ) diff --git a/synapse/models/biz.py b/synapse/models/biz.py index 474675acfa..6d941ef7bf 100644 --- a/synapse/models/biz.py +++ b/synapse/models/biz.py @@ -54,6 +54,36 @@ ('meta:taxonomy', {}), ), 'doc': 'A hierarchical taxonomy of product types.'}), + + ('biz:asked', ('entity:event', {}), { + 'template': {'title': 'ask'}, + 'interfaces': ( + ('biz:stance', {}), + ), + 'doc': 'An event where an actor made an ask as part of a negotiation.'}), + + ('biz:offered', ('entity:event', {}), { + 'template': {'title': 'offer'}, + 'interfaces': ( + ('biz:stance', {}), + ), + 'doc': 'An event where an actor made an offer as part of a negotiation.'}), + ), + + 'interfaces': ( + + ('biz:stance', { + 'props': ( + ('expires', ('time', {}), { + 'doc': 'The time that the {title} expires.'}), + + ('activity', ('biz:negotiable', {}), { + 'doc': 'The negotiation that this {title} is part of.'}), + ), + 'doc': 'An interface for asks and offers in a negotiation.'}), + + ('biz:negotiable', { + 'doc': 'An interface implemented by activities which involve negotiation.'}), ), 'edges': ( diff --git a/synapse/models/doc.py b/synapse/models/doc.py index 812c18e035..6c81701b88 100644 --- a/synapse/models/doc.py +++ b/synapse/models/doc.py @@ -68,6 +68,10 @@ 'doc': 'The name of the file containing the {title} contents.'}), ), }), + + ('doc:signable', { + 'doc': 'An interface applied to documents which are signed by individuals.'}), + ), 'types': ( @@ -139,10 +143,10 @@ ('doc:contract', ('guid', {}), { 'prevnames': ('ou:contract',), + 'template': {'title': 'contract'}, 'interfaces': ( - ('doc:document', {'template': { - 'title': 'contract', - 'type': 'doc:contract:type:taxonomy'}}), + ('doc:document', {}), + ('doc:signable', {}), ), 'doc': 'A contract between multiple entities.'}), @@ -153,9 +157,6 @@ ), 'doc': 'A hierarchical taxonomy of contract types.'}), - ('doc:document', ('ndef', {'interface': 'doc:document'}), { - 'doc': 'A node which implements the document interface.'}), - ('doc:reference', ('guid', {}), { 'doc': 'A reference included in a source.'}), @@ -208,10 +209,10 @@ ('workhist', ('array', {'type': 'ps:workhist'}), { 'doc': 'Work history described in the resume.'}), - ('education', ('array', {'type': 'ps:education'}), { + ('education', ('array', {'type': 'entity:educated'}), { 'doc': 'Education experience described in the resume.'}), - ('achievements', ('array', {'type': 'ps:achievement'}), { + ('achievements', ('array', {'type': 'entity:awarded'}), { 'doc': 'Achievements described in the resume.'}), )), @@ -244,9 +245,6 @@ ('parties', ('array', {'type': 'entity:actor'}), { 'doc': 'The entities bound by the contract.'}), - ('signers', ('array', {'type': 'entity:individual'}), { - 'doc': 'The individuals who signed the contract.'}), - ('period', ('ival', {}), { 'doc': 'The time period when the contract is in effect.'}), @@ -262,6 +260,7 @@ ('doc:reference', {}, ( + # FIXME make an interface for these somehow... ('source', ('ndef', {'forms': ('doc:report', 'risk:vuln', 'risk:tool:software', 'entity:campaign', 'meta:technique', 'plan:phase')}), { 'doc': 'The source which contains the reference.'}), diff --git a/synapse/models/economic.py b/synapse/models/economic.py index fcf93a2268..2390925760 100644 --- a/synapse/models/economic.py +++ b/synapse/models/economic.py @@ -146,8 +146,19 @@ ), 'doc': 'A Society for Worldwide Interbank Financial Telecommunication (SWIFT) Business Identifier Code (BIC).'}), - ('econ:pay:instrument', ('ndef', {'interface': 'econ:pay:instrument'}), { - 'doc': 'A node which may act as a payment instrument.'}), + ('econ:asked', ('biz:asked', {}), { + 'props': ( + ('price', ('econ:price', {}), { + 'doc': 'The price asked by the actor.'}), + ), + 'doc': 'A financial ask as part of a negotiation.'}), + + ('econ:offered', ('biz:offered', {}), { + 'props': ( + ('price', ('econ:price', {}), { + 'doc': 'The price offered by the actor.'}), + ), + 'doc': 'A financial ask as part of a negotiation.'}), ), 'interfaces': ( diff --git a/synapse/models/entity.py b/synapse/models/entity.py index 0e4562a51d..3f56e97187 100644 --- a/synapse/models/entity.py +++ b/synapse/models/entity.py @@ -8,35 +8,45 @@ ('entity:action', { 'template': {'title': 'action'}, - 'doc': 'Properties which are common to actions taken by entities.', 'props': ( - ('actor', ('entity:actor', {}), { 'doc': 'The actor who carried out the {title}.'}), ('actor:name', ('entity:name', {}), { 'doc': 'The name of the actor who carried out the {title}.'}), - ), - }), - ('entity:attendable', { - 'template': {'title': 'event'}, - 'interfaces': ( - ('geo:locatable', {}), - ('lang:transcript', {}), + ('actor:roles', ('array', {'type': 'base:name'}), { + 'doc': 'The roles of the actor in the {title}.'}), + ), - 'props': ( - ('desc', ('text', {}), { - 'doc': 'A description of the {title}.'}), + 'doc': 'Properties common to actions taken by an individual actor.'}), - ('period', ('ival', {}), { - 'doc': 'The period of time over which the {title} occurred.'}), + ('entity:affected', { + 'props': ( + ('party', ('entity:actor', {}), { + 'doc': 'The party which was affected.'}), - ('parent', ('entity:attendable', {}), { - 'doc': 'The parent event which hosts the {title}.'}), + ('party:name', ('entity:name', {}), { + 'doc': 'The name of the party which was affected.'}), ), - 'doc': 'Properties common to events which individuals may attend.', - }), + 'doc': 'An interface used for events which affect or impact an entity.'}), + + # ('entity:affected', { + # 'template': {'affected': 'affected'}, + # 'props': ( + # ('event', ('meta:causal', {}), { + # 'doc': 'The event which affected the entity.'}), + + # ('party', ('entity:actor', {}), { + # 'doc': 'The entity who was {affected}.'}), + + # ('party:name', ('entity:name', {}), { + # 'doc': 'The name of the entity who was {affected}.'}), + + # ('period', ('ival', {}), { + # 'doc': 'The period over which the entity was {affected}.'}), + # ), + # 'doc': 'Properties common to entities being affected by an event.'}), ('entity:contactable', { @@ -147,7 +157,7 @@ ('entity:multiple', { 'doc': 'Properties which apply to entities which may represent a group or organization.'}), - ('entity:abstract', { + ('entity:resolvable', { 'template': {'title': 'entity'}, 'props': ( ('resolved', ('entity:resolved', {}), { @@ -158,30 +168,19 @@ 'types': ( - ('entity:attendable', ('ndef', {'interface': 'entity:attendable'}), { - 'doc': 'An event where individuals may attend or participate.'}), - - ('entity:contactable', ('ndef', {'interface': 'entity:contactable'}), { - 'doc': 'A node which implements the entity:contactable interface.'}), - ('entity:resolved', ('ndef', {'forms': ('ou:org', 'ps:person')}), { 'doc': 'A fully resolved entity such as a person or organization.'}), ('entity:individual', ('ndef', {'forms': ('ps:person', 'entity:contact', 'inet:service:account')}), { 'doc': 'A singular entity such as a person.'}), - ('entity:identifier', ('ndef', {'interface': 'entity:identifier'}), { - 'doc': 'A node which inherits the entity:identifier interface.'}), - ('entity:name', ('base:name', {}), { + 'props': (), 'doc': 'A name used to refer to an entity.'}), - # FIXME syn:user is an actor... - ('entity:actor', ('ndef', {'interface': 'entity:actor'}), { - 'doc': 'An entity which has initiative to act.'}), - ('entity:title', ('str', {'onespace': True, 'lower': True}), { 'prevnames': ('ou:jobtitle', 'ou:role'), + 'props': (), 'doc': 'A title or position name used by an entity.'}), ('entity:contact:type:taxonomy', ('taxonomy', {}), { @@ -196,7 +195,7 @@ ('entity:actor', {}), ('entity:singular', {}), ('entity:multiple', {}), - ('entity:abstract', {}), + ('entity:resolvable', {}), ('entity:contactable', {}), ('meta:observable', {}), ), @@ -218,9 +217,43 @@ ), 'doc': 'Historical contact information about another contact.'}), + ('entity:alliance', ('guid', {}), { + 'template': {'title': 'alliance'}, + 'interfaces': ( + ('entity:actor', {}), + ('meta:reported', {}), + ), + 'props': ( + ('name', ('entity:name', {}), { + 'alts': ('names',), + 'doc': 'The primary name of the {title}.'}), + + ('names', ('array', {'type': 'entity:name'}), { + 'doc': 'A list of alternate names for the {title}.'}), + + ('members', ('array', {'type': 'entity:actor'}), { + 'doc': 'The actors who are working together.'}), + ), + 'doc': 'An alliance of otherwise distinct actors working together.'}), + ('entity:contactlist', ('guid', {}), { 'doc': 'A list of contacts.'}), + ('entity:event', ('guid', {}), { + 'interfaces': ( + ('base:event', {}), + ('entity:action', {}), + ), + 'doc': 'An event carried out by an actor.'}), + + ('entity:activity', ('guid', {}), { + 'interfaces': ( + ('base:activity', {}), + ('entity:action', {}), + ), + 'props': (), + 'doc': 'Activity carried out by an actor.'}), + ('entity:relationship:type:taxonomy', ('taxonomy', {}), { 'interfaces': ( ('meta:taxonomy', {}), @@ -246,24 +279,19 @@ ), 'doc': 'A hierarchical taxonomy of types of possession.'}), - ('entity:had', ('guid', {}), { + ('entity:had', ('entity:activity', {}), { 'doc': 'An item which was possessed by an actor.'}), - ('entity:attendee', ('guid', {}), { - 'doc': 'A person attending an event.'}), - - ('entity:conversation', ('guid', {}), { - 'doc': 'A conversation between entities.'}), + # ('entity:conversation', ('guid', {}), { + # 'doc': 'A conversation between entities.'}), - # FIXME entity:goal needs an interface ( for extensible goals without either/or props? ) - # FIXME entity:goal needs to clearly differentiate actor/action goals vs goal types - # FIXME entity:goal should consider a backlink to entity:actor/entity:action SO specifics ('entity:goal:type:taxonomy', ('taxonomy', {}), { 'interfaces': ( ('meta:taxonomy', {}), ), 'doc': 'A hierarchical taxonomy of goal types.'}), + # FIXME :status properties need some review ('entity:goal:status:taxonomy', ('taxonomy', {}), { 'interfaces': ( ('meta:taxonomy', {}), @@ -286,18 +314,18 @@ 'interfaces': ( ('meta:taxonomy', {}), ), + 'props': (), 'doc': 'A hierarchical taxonomy of campaign types.'}), - ('entity:campaign:status:taxonomy', ('taxonomy', {}), { - 'interfaces': ( - ('meta:taxonomy', {}), - ), - 'doc': 'A hierarchical taxonomy of campaign statuses.'}), + # ('entity:campaign:status:taxonomy', ('taxonomy', {}), { + # 'interfaces': ( + # ('meta:taxonomy', {}), + # ), + # 'doc': 'A hierarchical taxonomy of campaign statuses.'}), - ('entity:campaign', ('guid', {}), { + ('entity:campaign', ('entity:activity', {}), { 'template': {'title': 'campaign'}, 'interfaces': ( - ('entity:action', {}), ('meta:reported', {}), ), 'display': { @@ -311,19 +339,130 @@ }, 'doc': 'Activity in pursuit of a goal.'}), - ('entity:conflict', ('guid', {}), { - 'doc': 'Represents a conflict where two or more campaigns have mutually exclusive goals.'}), + ('entity:conflict', ('meta:activity', {}), { + 'props': ( + ('adversaries', ('array', {'type': 'entity:actor'}), { + 'doc': 'The primary adversaries in conflict.'}), + ), + 'doc': 'Represents a conflict where two or more actors have mutually exclusive goals.'}), + + # ('entity:affected', ('meta:activity', {}), { + # 'props': ( + # ('party', ('entity:actor', {}), { + # 'doc': 'The party which was affected.'}), + + # ('party:name', ('entity:name', {}), { + # 'doc': 'The name of the party which was affected.'}), + # ), + # 'doc': 'An entity which was affected by events.'}), + + # entity:knew / entity:awareof? + # ('entity:observed', ('entity:affected', {}), { + # 'interfaces': ( + # ('entity:affected', {}), + # ), + # 'props': ( + # ('event', ('meta:causal', {}), { + # 'doc': 'The event which was observed by the entity.'}), + # ), + # 'doc': 'Passive observation of an event by an entity.'}), + + ('entity:participated', ('entity:activity', {}), { + 'props': ( + ('event', ('meta:causal', {}), { + 'doc': 'The event or activity the actor was involved in.'}), + ), + 'doc': "Represents an actor's active involvement with an event."}), + + # ('entity:support', ('entity:involved', {}), { + # 'template': {'title': 'support'}, + # 'doc': 'Represents an actor having materially supported an event.'}), + # + # ('entity:contribution:type:taxonomy', ('taxonomy', {}), { + # 'doc': 'A hierarchical taxonomy of contribution types.'}), - ('entity:contribution', ('guid', {}), { + ('entity:contribution', ('entity:event', {}), { 'template': {'title': 'contribution'}, - 'interfaces': ( - ('entity:action', {}), + 'props': ( + ('event', ('entity:activity', {}), { + 'doc': 'The event or activity which the actor contributed to.'}), + + ('value', ('econ:price', {}), { + 'doc': 'The total value of the actors contribution.'}), ), - 'doc': 'Represents a specific instance of contributing material support to a campaign.'}), + 'doc': 'An actor providing support for an event or activity.'}), - ('entity:discovery', ('guid', {}), { + ('entity:discovered', ('entity:event', {}), { + 'templates': {'title': 'discovery'}, + 'props': ( + ('item', ('meta:discoverable', {}), { + 'doc': 'The item which was discovered.'}), + ), 'doc': 'A discovery made by an actor.'}), + # TODO: entity:obeyed? ( for -(followed)> tenet ) + ('entity:believed', ('entity:activity', {}), { + 'prevnames': ('belief:subscriber',), + 'props': ( + ('belief', ('meta:believable', {}), { + 'doc': 'The belief held by the actor.'}), + ), + 'doc': 'A belief held by an actor.'}), + + ('entity:competed', ('entity:activity', {}), { + 'prevnames': ('ou:contest:result',), + 'props': ( + ('activity', ('meta:competitive', {}), { + 'doc': 'The competition that the actor competed in.'}), + + ('url', ('inet:url', {}), { + 'doc': 'A URL which documents the actors results.'}), + + ('rank', ('int', {}), { + 'doc': "The actor's rank order in the contest."}), + + ('score', ('int', {}), { + 'doc': "The actor's final score in the contest."}), + ), + 'doc': 'An event where an actor competed in an organized competition.'}), + + ('entity:educated', ('entity:activity', {}), { + 'props': ( + # TODO: this will probably need to be expanded to include entity:contact with poly + ('institution', ('ou:org', {}), { + 'doc': 'The institution providing educational services.'}), + ), + 'doc': 'An actor participating in formal education such as school or training.'}), + + ('entity:awarded', ('entity:event', {}), { + 'props': ( + ('award', ('meta:awardable', {}), { + 'doc': 'The award or achievement which the actor was awarded.'}), + ), + 'doc': 'An event where an actor was granted an award.'}), + + ('entity:attended', ('entity:activity', {}), { + 'props': ( + ('event', ('meta:attendable', {}), { + 'doc': 'The event or activity attended by the actor.'}), + ), + 'doc': 'An actor attending an event.'}), + + ('entity:registered', ('entity:event', {}), { + 'props': ( + ('contact', ('entity:contact', {}), { + 'doc': 'The contact information provided by the actor.'}), + ), + 'doc': 'An event where an actor registered to attend an event.'}), + + ('entity:signed', ('entity:event', {}), { + 'props': ( + ('document', ('doc:signable', {}), { + 'doc': 'The document which was signed by the actor.'}), + ), + 'doc': 'An event where an actor signed a document.'}), + + ), 'edges': ( @@ -348,19 +487,15 @@ (('entity:action', 'had', 'entity:goal'), { 'doc': 'The action was taken in pursuit of the goal.'}), - (('entity:contribution', 'had', 'econ:lineitem'), { - 'doc': 'The contribution includes the line item.'}), + # (('entity:contributed', 'had', 'econ:lineitem'), { + # 'doc': 'The contribution includes the line item.'}), - (('entity:contribution', 'had', 'econ:payment'), { - 'doc': 'The contribution includes the payment.'}), + # (('entity:contributed', 'had', 'econ:payment'), { + # 'doc': 'The contribution includes the payment.'}), ), 'forms': ( - ('entity:title', {}, ()), - - ('entity:name', {}, ()), - ('entity:contact:type:taxonomy', {}, ()), ('entity:contact', {}, ( @@ -421,26 +556,6 @@ 'doc': 'The target entity in the relationship.'}), )), - ('entity:attendee', {}, ( - - ('person', ('entity:individual', {}), { - 'doc': 'The person who attended the event.'}), - - ('period', ('ival', {}), { - 'doc': 'The time period when the person attended the event.'}), - - ('roles', ('array', {'type': 'base:name', 'split': ','}), { - 'doc': 'List of the roles the person had at the event.'}), - - ('event', ('entity:attendable', {}), { - 'prevnames': ('meet', 'conference', 'conference:event', 'contest', 'preso'), - 'doc': 'The event that the person attended.'}), - - # ('link', ('entity:link', {}), { - # 'doc': 'The remote communication mechanism used by the person to attend the event.'}), - )), - - ('entity:goal:type:taxonomy', {}, ()), ('entity:goal', {}, ( @@ -458,31 +573,21 @@ 'doc': 'A description of the goal.'}), )), - ('entity:campaign:type:taxonomy', { - 'prevnames': ('ou:camptype',)}, ()), - ('entity:campaign', {}, ( ('slogan', ('lang:phrase', {}), { 'doc': 'The slogan used by the campaign.'}), - ('actors', ('array', {'type': 'entity:actor', 'split': ','}), { - 'doc': 'Actors who participated in the campaign.'}), - ('success', ('bool', {}), { 'doc': 'Set to true if the campaign achieved its goals.'}), - # TODO: should we create risk:campaign and define this there ('sophistication', ('meta:score', {}), { 'doc': 'The assessed sophistication of the campaign.'}), ('type', ('entity:campaign:type:taxonomy', {}), { - 'doc': 'A type taxonomy entry for the campaign.', - 'prevnames': ('camptype',)}), - - ('period', ('ival', {}), { - 'doc': 'The time interval when the entity was running the campaign.'}), + 'doc': 'A type taxonomy entry for the campaign.'}), + # TODO: cost:budget cost:actual ? ('cost', ('econ:price', {}), { 'protocols': { 'econ:adjustable': {'props': {'time': 'period.min', 'currency': 'currency'}}, @@ -498,56 +603,9 @@ ('currency', ('econ:currency', {}), { 'doc': 'The currency used to record econ:price properties.'}), - ('team', ('ou:team', {}), { - 'doc': 'The org team responsible for carrying out the campaign.'}), - - # FIXME overfit? - ('conflict', ('entity:conflict', {}), { - 'doc': 'The conflict in which this campaign is a primary participant.'}), - ('tag', ('syn:tag', {}), { 'doc': 'The tag used to annotate nodes that are associated with the campaign.'}), )), - - ('entity:conflict', {}, ( - - ('name', ('meta:name', {}), { - 'doc': 'The name of the conflict.'}), - - ('period', ('ival', {}), { - 'doc': 'The period of time when the conflict was ongoing.'}), - - ('adversaries', ('array', {'type': 'entity:actor'}), { - 'doc': 'The primary adversaries in conflict with one another.'}), - )), - ('entity:contribution', {}, ( - - ('campaign', ('entity:campaign', {}), { - 'doc': 'The campaign receiving the contribution.'}), - - # FIXME - :price / :price:currency ( and the interface ) - ('value', ('econ:price', {}), { - 'doc': 'The assessed value of the contribution.'}), - - ('currency', ('econ:currency', {}), { - 'doc': 'The currency used for the assessed value.'}), - - ('time', ('time', {}), { - 'doc': 'The time the contribution occurred.'}), - )), - - ('entity:discovery', {}, ( - - ('actor', ('entity:actor', {}), { - 'doc': 'The actor who made the discovery.'}), - - ('time', ('time', {}), { - 'doc': 'The time when the discovery was made.'}), - - ('item', ('meta:discoverable', {}), { - 'doc': 'The item which was discovered.'}), - )), - ), }), ) diff --git a/synapse/models/geopol.py b/synapse/models/geopol.py index e661937c52..dc08b019ba 100644 --- a/synapse/models/geopol.py +++ b/synapse/models/geopol.py @@ -33,10 +33,50 @@ ('pol:office', ('guid', {}), { 'doc': 'An elected or appointed office.'}), - ('pol:term', ('guid', {}), { + ('pol:term', ('entity:activity', {}), { + 'props': ( + ('office', ('pol:office', {}), { + 'doc': 'The office held for the term.'}), + + ('race', ('pol:race', {}), { + 'doc': 'The race that determined who held office during the term.'}), + + ('party', ('ou:org', {}), { + 'doc': 'The political party of the person who held office during the term.'}), + ), 'doc': 'A term in office held by a specific individual.'}), - ('pol:candidate', ('guid', {}), { + ('pol:candidacy', ('entity:activity', {}), { + 'template': {'title': 'candidacy'}, + 'prevnames': ('pol:candidate',), + 'props': ( + ('actor', ('entity:actor', {}), { + 'doc': 'The actor running for office.'}), + + ('actor:name', ('entity:name', {}), { + 'doc': 'The name of the actor running for office.'}), + + ('period', ('ival', {}), { + 'doc': 'The period when the actor was a candidate.'}), + + ('id', ('meta:id', {}), { + 'doc': 'A unique ID for the candidate issued by an election authority.'}), + + ('race', ('pol:race', {}), { + 'doc': 'The race the candidate is participating in.'}), + + ('campaign', ('entity:campaign', {}), { + 'doc': 'The official campaign to elect the candidate.'}), + + ('winner', ('bool', {}), { + 'doc': 'Records the outcome of the race.'}), + + ('party', ('ou:org', {}), { + 'doc': 'The declared political party of the candidate.'}), + + ('incumbent', ('bool', {}), { + 'doc': 'Set to true if the candidate is an incumbent in this race.'}), + ), 'doc': 'A candidate for office in a specific race.'}), ('pol:pollingplace', ('guid', {}), { @@ -169,47 +209,6 @@ ('govbody', ('ou:org', {}), { 'doc': 'The governmental body which contains the office.'}), )), - ('pol:term', {}, ( - - ('office', ('pol:office', {}), { - 'doc': 'The office held for the term.'}), - - ('period', ('ival', {}), { - 'prevnames': ('start', 'end'), - 'doc': 'The time period of the term of office.'}), - - ('race', ('pol:race', {}), { - 'doc': 'The race that determined who held office during the term.'}), - - ('contact', ('entity:contact', {}), { - 'doc': 'The contact information of the person who held office during the term.'}), - - ('party', ('ou:org', {}), { - 'doc': 'The political party of the person who held office during the term.'}), - )), - ('pol:candidate', {}, ( - - ('id', ('meta:id', {}), { - 'doc': 'A unique ID for the candidate issued by an election authority.'}), - - ('contact', ('entity:contact', {}), { - 'doc': 'The contact information of the candidate.'}), - - ('race', ('pol:race', {}), { - 'doc': 'The race the candidate is participating in.'}), - - ('campaign', ('entity:campaign', {}), { - 'doc': 'The official campaign to elect the candidate.'}), - - ('winner', ('bool', {}), { - 'doc': 'Records the outcome of the race.'}), - - ('party', ('ou:org', {}), { - 'doc': 'The declared political party of the candidate.'}), - - ('incumbent', ('bool', {}), { - 'doc': 'Set to true if the candidate is an incumbent in this race.'}), - )), ('pol:pollingplace', {}, ( ('election', ('pol:election', {}), { diff --git a/synapse/models/inet.py b/synapse/models/inet.py index f6fa8f0855..ccfad636e6 100644 --- a/synapse/models/inet.py +++ b/synapse/models/inet.py @@ -1953,7 +1953,7 @@ async def _onSetFqdnZone(node): 'template': {'title': 'subscriber'}, 'interfaces': ( ('entity:actor', {}), - ('entity:abstract', {}), + ('entity:resolvable', {}), ('inet:service:object', {}), ), 'props': ( diff --git a/synapse/models/language.py b/synapse/models/language.py index e165af2c22..fa420fbc0c 100644 --- a/synapse/models/language.py +++ b/synapse/models/language.py @@ -2,6 +2,7 @@ ('lang', { 'interfaces': ( + # TODO: lang:transcribable ('lang:transcript', { 'doc': 'An interface which applies to forms containing speech.', }), diff --git a/synapse/models/orgs.py b/synapse/models/orgs.py index 5990cc4751..0000ae02cb 100644 --- a/synapse/models/orgs.py +++ b/synapse/models/orgs.py @@ -11,52 +11,7 @@ modeldefs = ( ('ou', { - 'interfaces': ( - - ('ou:attendable', { - 'template': {'title': 'event'}, - 'interfaces': ( - ('meta:havable', {}), - ('entity:attendable', {}), - ), - 'props': ( - - ('name', ('meta:name', {}), { - 'alts': ('names',), - 'ex': 'cyberwarcon 2025', - 'doc': 'The name of the {title}.'}), - - ('name:base', ('meta:name', {}), { - 'ex': 'cyberwarcon', - 'doc': 'The base name of the {title} (for a recurring event).'}), - - ('names', ('array', {'type': 'meta:name'}), { - 'doc': 'An array of alternate names for the {title}.'}), - ), - 'doc': 'An interface which is inherited by all organized events.'}), - - ('ou:sponsored', { - 'template': {'title': 'event'}, - 'interfaces': ( - ('ou:attendable', {}), - ), - 'props': ( - - ('website', ('inet:url', {}), { - 'prevnames': ('url',), - 'doc': 'The URL of the {title} website.'}), - - ('contact', ('entity:contact', {}), { - 'doc': 'Contact information for the {title}.'}), - - ('sponsors', ('array', {'type': 'entity:actor'}), { - 'doc': 'The entities which sponsored the {title}.'}), - - ('organizers', ('array', {'type': 'entity:actor'}), { - 'doc': 'An array of {title} organizers.'}), - ), - 'doc': 'Properties which are common to events which are hosted or sponsored by organizations.'}), - ), + 'interfaces': (), 'types': ( ('ou:sic', ('str', {'regex': r'^[0-9]{4}$'}), { 'ex': '0111', @@ -157,81 +112,82 @@ ('ou:position', ('guid', {}), { 'doc': 'A position within an org which can be organized into an org chart with replaceable contacts.'}), - ('ou:meeting', ('guid', {}), { - 'prevnames': ('ou:meet',), + ('ou:event', ('meta:activity', {}), { + 'template': {'title': 'organized event'}, 'interfaces': ( - ('ou:attendable', {'template': {'title': 'meeting'}}), + ('geo:locatable', {}), + ('lang:transcript', {}), + ('meta:attendable', {}), + ('meta:promotable', {}), ), - 'doc': 'A meeting.'}), + 'props': (), + 'doc': 'An organized event without a more specific form.'}), - ('ou:preso', ('guid', {}), { + ('ou:meeting', ('meta:activity', {}), { + 'template': {'title': 'meeting'}, 'interfaces': ( - ('ou:sponsored', {'template': {'title': 'presentation'}}), + ('geo:locatable', {}), + ('lang:transcript', {}), + ('meta:attendable', {}), ), - 'display': { - 'columns': ( - {'type': 'prop', 'opts': {'name': 'period'}}, - {'type': 'prop', 'opts': {'name': 'name'}}, - {'type': 'prop', 'opts': {'name': 'parent::name'}}, - ), - }, - 'doc': 'A webinar, conference talk, or other type of presentation.'}), + 'props': (), + 'doc': 'An organized meeting.'}), - ('ou:conference', ('guid', {}), { - 'template': {'title': 'conference'}, + ('ou:preso', ('meta:activity', {}), { + 'template': {'title': 'presentation'}, 'interfaces': ( - ('ou:sponsored', {}), + ('geo:locatable', {}), + ('lang:transcript', {}), + ('meta:promotable', {}), + ('meta:attendable', {}), + ('meta:sponsorable', {}), ), 'display': { 'columns': ( {'type': 'prop', 'opts': {'name': 'name'}}, - {'type': 'prop', 'opts': {'name': 'website'}}, - # TODO allow columns to use virtual props - # {'type': 'prop', 'opts': {'name': 'period.min'}}, - # {'type': 'prop', 'opts': {'name': 'period.max'}}, + {'type': 'prop', 'opts': {'name': 'period'}}, ), }, - 'doc': 'A conference.'}), + 'doc': 'A webinar, conference talk, or other type of presentation.'}), - ('ou:event:type:taxonomy', ('taxonomy', {}), { + ('ou:conference', ('meta:activity', {}), { + 'template': {'title': 'conference'}, 'interfaces': ( - ('meta:taxonomy', {}), + ('geo:locatable', {}), + ('meta:promotable', {}), + ('meta:attendable', {}), + ('meta:sponsorable', {}), ), - 'doc': 'A hierarchical taxonomy of event types.'}), - - ('ou:event', ('guid', {}), { - 'template': {'title': 'event'}, - 'prevnames': ('ou:conference:event',), - 'interfaces': ( - ('ou:sponsored', {}), + 'props': ( + ('family', ('base:name', {}), { + 'doc': 'The name of the family of conferences.'}), ), 'display': { 'columns': ( {'type': 'prop', 'opts': {'name': 'name'}}, - {'type': 'prop', 'opts': {'name': 'parent::name'}}, + {'type': 'prop', 'opts': {'name': 'website'}}, # TODO allow columns to use virtual props # {'type': 'prop', 'opts': {'name': 'period.min'}}, # {'type': 'prop', 'opts': {'name': 'period.max'}}, ), }, - 'doc': 'An generic organized event.'}), - - ('ou:contest:type:taxonomy', ('taxonomy', {}), { - 'interfaces': ( - ('meta:taxonomy', {}), - ), - 'doc': 'A hierarchical taxonomy of contest types.'}), + 'doc': 'A conference.'}), - ('ou:contest', ('guid', {}), { + ('ou:contest', ('meta:activity', {}), { 'template': {'title': 'contest'}, 'interfaces': ( - ('ou:sponsored', {}), + ('geo:locatable', {}), + ('meta:promotable', {}), + ('meta:attendable', {}), + ('meta:sponsorable', {}), + ('meta:competitive', {}), + ), + 'props': ( + ('family', ('base:name', {}), { + 'doc': 'The name of the family of contests.'}), ), 'doc': 'A competitive event resulting in a ranked set of participants.'}), - ('ou:contest:result', ('guid', {}), { - 'doc': 'The results from a single contest participant.'}), - ('ou:id', ('guid', {}), { 'interfaces': ( ('meta:observable', {'template': {'title': 'ID'}}), @@ -254,15 +210,6 @@ 'prevnames': ('ou:id:update',), 'doc': 'Changes made to an ID over time.'}), - ('ou:award:type:taxonomy', ('taxonomy', {}), { - 'interfaces': ( - ('meta:taxonomy', {}), - ), - 'doc': 'A hierarchical taxonomy of award types.'}), - - ('ou:award', ('guid', {}), { - 'doc': 'An award issued by an organization.'}), - ('ou:vitals', ('guid', {}), { 'doc': 'Vital statistics about an org for a given time period.'}), @@ -318,6 +265,7 @@ 'interfaces': ( ('meta:taxonomy', {}), ), + 'props': (), 'doc': 'A hierarchical taxonomy of employment types.'}), ('ou:enacted:status:taxonomy', ('taxonomy', {}), { @@ -346,9 +294,6 @@ ('ou:job:type:taxonomy', { 'prevnames': ('ou:jobtype',)}, ()), - ('ou:employment:type:taxonomy', { - 'prevnames': ('ou:employment',)}, ()), - ('ou:opening', {}, ( ('org', ('ou:org', {}), { @@ -515,21 +460,6 @@ ('delta:population', ('int', {}), { 'doc': 'The change in population over last period.'}), )), - ('ou:award:type:taxonomy', {}, ()), - ('ou:award', {}, ( - - ('name', ('meta:name', {}), { - 'doc': 'The name of the award.', - 'ex': 'Bachelors of Science'}), - - ('type', ('ou:award:type:taxonomy', {}), { - 'doc': 'The type of award.', - 'ex': 'certification'}), - - ('org', ('ou:org', {}), { - 'doc': 'The organization which issues the award.'}), - - )), ('ou:id:type:taxonomy', {}, ()), ('ou:id:status:taxonomy', {}, ()), @@ -711,9 +641,10 @@ )), ('ou:preso', {}, ( - ('presenters', ('array', {'type': 'entity:individual'}), { + ('presenters', ('array', {'type': 'entity:actor'}), { 'doc': 'An array of individuals who gave the presentation.'}), + # TODO: multiple and context ('deck:url', ('inet:url', ()), { 'doc': 'The URL hosting a copy of the presentation materials.'}), @@ -723,49 +654,13 @@ ('attendee:url', ('inet:url', ()), { 'doc': 'The URL visited by live attendees of the presentation.'}), + # TODO: should something like this be added to lang:transcript? lang:recording ('recording:url', ('inet:url', ()), { 'doc': 'The URL hosting a recording of the presentation.'}), ('recording:file', ('file:bytes', ()), { 'doc': 'A file containing a recording of the presentation.'}), )), - ('ou:meeting', {}, ( - ('hosts', ('array', {'type': 'entity:actor'}), { - 'doc': 'The contacts who hosted or called the meeting.'}), - )), - ('ou:conference', {}, ()), - ('ou:event', {}, ( - ('type', ('ou:event:type:taxonomy', {}), { - 'doc': 'The type of event.'}), - )), - ('ou:contest:type:taxonomy', {}, ()), - ('ou:contest', {}, ( - - ('type', ('ou:contest:type:taxonomy', {}), { - 'ex': 'cyber.ctf', - 'doc': 'The type of contest.'}), - - )), - ('ou:contest:result', {}, ( - - ('contest', ('ou:contest', {}), { - 'doc': 'The contest that the participant took part in.'}), - - ('participant', ('entity:actor', {}), { - 'doc': 'The participant in the contest.'}), - - ('rank', ('int', {}), { - 'doc': "The participant's rank order in the contest."}), - - ('score', ('int', {}), { - 'doc': "The participant's final score in the contest."}), - - ('period', ('ival', {}), { - 'doc': 'The period of time when the participant competed in the contest.'}), - - ('url', ('inet:url', {}), { - 'doc': "A URL which documents the participant's results."}), - )), ('ou:enacted:status:taxonomy', {}, ()), ('ou:enacted', {}, ( diff --git a/synapse/models/person.py b/synapse/models/person.py index ec751315e3..d52d93b60b 100644 --- a/synapse/models/person.py +++ b/synapse/models/person.py @@ -8,34 +8,13 @@ 'doc': 'A course of study taught by an org.'}), ('edu:class', ('guid', {}), { + 'template': {'title': 'class'}, 'interfaces': ( - ('ou:attendable', {'template': {'title': 'class'}}), + ('geo:locatable', {}), + ('meta:attendable', {}), ), 'doc': 'An instance of an edu:course taught at a given time.'}), - ('ps:education', ('guid', {}), { - 'display': { - 'columns': ( - {'type': 'prop', 'opts': {'name': 'student::name'}}, - {'type': 'prop', 'opts': {'name': 'institution::name'}}, - # TODO allow columns to use virtual props - # {'type': 'prop', 'opts': {'name': 'period.min'}}, - # {'type': 'prop', 'opts': {'name': 'period.max'}}, - ), - }, - 'doc': 'A period of education for an individual.'}), - - ('ps:achievement', ('guid', {}), { - 'display': { - 'columns': ( - {'type': 'prop', 'opts': {'name': 'awardee::name'}}, - {'type': 'prop', 'opts': {'name': 'award::name'}}, - {'type': 'prop', 'opts': {'name': 'award::org::name'}}, - {'type': 'prop', 'opts': {'name': 'awarded'}}, - ), - }, - 'doc': 'An instance of an individual receiving an award.'}), - ('ps:person', ('guid', {}), { 'template': {'title': 'person'}, 'interfaces': ( @@ -45,12 +24,13 @@ ), 'doc': 'A person or persona.'}), - ('ps:workhist', ('guid', {}), { + # TODO: entity:workhist? entity:worked? (supported + pay?) + ('ps:workhist', ('entity:activity', {}), { 'display': { 'columns': ( - {'type': 'prop', 'opts': {'name': 'contact::name'}}, + {'type': 'prop', 'opts': {'name': 'actor:name'}}, {'type': 'prop', 'opts': {'name': 'title'}}, - {'type': 'prop', 'opts': {'name': 'org:name'}}, + {'type': 'prop', 'opts': {'name': 'employer:name'}}, # TODO allow columns to use virtual props # {'type': 'prop', 'opts': {'name': 'period.min'}}, # {'type': 'prop', 'opts': {'name': 'period.max'}}, @@ -104,26 +84,24 @@ ), 'edges': ( - (('ps:education', 'included', 'edu:class'), { + (('entity:educated', 'included', 'edu:class'), { 'doc': 'The class was taken by the student as part of their education process.'}), ), 'forms': ( ('ps:workhist', {}, ( - ('contact', ('entity:individual', {}), { - 'doc': 'The contact which has the work history.'}), - - ('org', ('ou:org', {}), { - 'doc': 'The org that this work history orgname refers to.'}), + ('employer', ('entity:actor', {}), { + 'prevnames': ('org',), + 'doc': 'The employer.'}), - ('org:name', ('entity:name', {}), { + ('employer:name', ('entity:name', {}), { 'prevnames': ('orgname',), - 'doc': 'The reported name of the org the contact worked for.'}), + 'doc': 'The name of the employer.'}), - ('org:fqdn', ('inet:fqdn', {}), { + ('employer:fqdn', ('inet:fqdn', {}), { 'prevnames': ('orgfqdn',), - 'doc': 'The reported fqdn of the org the contact worked for.'}), + 'doc': 'The FQDN of the employer.'}), ('job:type', ('ou:job:type:taxonomy', {}), { 'doc': 'The type of job.', @@ -145,7 +123,7 @@ ('period', ('ival', {}), { 'prevnames': ('started', 'ended', 'duration'), - 'doc': 'The period of time that the contact worked for the organization.'}), + 'doc': 'The period of time that the actor worked for the employer.'}), ('desc', ('str', {}), { 'doc': 'A description of the work done as part of the job.'}), @@ -176,6 +154,7 @@ ('course', ('edu:course', {}), { 'doc': 'The course being taught in the class.'}), + # TODO: should these become entity:participated to capture times? ('instructor', ('entity:individual', {}), { 'doc': 'The primary instructor for the class.'}), @@ -195,41 +174,6 @@ ('virtual:provider', ('entity:actor', {}), { 'doc': 'Contact info for the virtual infrastructure provider.'}), )), - ('ps:education', {}, ( - - ('student', ('entity:individual', {}), { - 'doc': 'The student who attended the educational institution.'}), - - ('institution', ('ou:org', {}), { - 'doc': 'The organization providing educational services.'}), - - ('period', ('ival', {'precision': 'day'}), { - 'prevnames': ('attended:first', 'attended:last'), - 'doc': 'The period of time when the student attended the institution.'}), - - ('achievement', ('ps:achievement', {}), { - 'doc': 'The degree or certificate awarded to the individual.'}), - - )), - ('ps:achievement', {}, ( - - ('awardee', ('entity:individual', {}), { - 'doc': 'The recipient of the award.'}), - - ('award', ('ou:award', {}), { - 'doc': 'The award bestowed on the awardee.'}), - - ('awarded', ('time', {}), { - 'doc': 'The date the award was granted to the awardee.'}), - - ('expires', ('time', {}), { - 'doc': 'The date the award or certification expires.'}), - - ('revoked', ('time', {}), { - 'doc': 'The date the award was revoked by the org.'}), - - )), - ('ps:person', {}, ( ('vitals', ('ps:vitals', {}), { 'doc': 'The most recent vitals for the person.'}), diff --git a/synapse/models/risk.py b/synapse/models/risk.py index 85194b0a0e..5c158818fc 100644 --- a/synapse/models/risk.py +++ b/synapse/models/risk.py @@ -84,7 +84,7 @@ async def _normPyStr(self, text, view=None): ('meta:reported', {}), ('meta:discoverable', {}), ('entity:actor', {}), - ('entity:abstract', {}), + ('entity:resolvable', {}), ('entity:contactable', {}), ), 'doc': 'A threat cluster or subgraph of threat activity, as defined by a specific source.', @@ -104,19 +104,28 @@ async def _normPyStr(self, text, view=None): ), 'doc': 'A hierarchical taxonomy of threat statuses.'}), - ('risk:attack', ('guid', {}), { + ('risk:attack', ('entity:event', {}), { 'template': {'title': 'attack'}, 'interfaces': ( - ('entity:action', {}), ('meta:reported', {}), + ('meta:discoverable', {}), ), - 'doc': 'An instance of an actor attacking a target.'}), + 'props': ( - ('risk:attack:status:taxonomy', ('taxonomy', {}), { - 'interfaces': ( - ('meta:taxonomy', {}), + ('type', ('risk:attack:type:taxonomy', {}), { + 'ex': 'cno.phishing', + 'doc': 'A type for the attack, as a taxonomy entry.'}), + + ('success', ('bool', {}), { + 'doc': 'Set if the attack was known to have succeeded or not.'}), + + ('severity', ('meta:score', {}), { + 'doc': 'A severity rank for the attack.'}), + + ('sophistication', ('meta:score', {}), { + 'doc': 'The assessed sophistication of the attack.'}), ), - 'doc': 'A hierarchical taxonomy of attack statuses.'}), + 'doc': 'An instance of an actor attacking a target.'}), ('risk:alert:type:taxonomy', ('taxonomy', {}), { 'interfaces': ( @@ -133,11 +142,12 @@ async def _normPyStr(self, text, view=None): ), 'doc': 'A taxonomy of compromise statuses.'}), - ('risk:compromise', ('guid', {}), { + ('risk:compromise', ('entity:activity', {}), { 'template': {'title': 'compromise'}, 'interfaces': ( ('meta:reported', {}), - ('entity:action', {}), + ('risk:victimized', {}), + ('meta:discoverable', {}), ), 'display': { 'columns': ( @@ -233,17 +243,17 @@ async def _normPyStr(self, text, view=None): ), 'doc': 'A hierarchical taxonomy of threat types.'}), - ('risk:leak', ('guid', {}), { + ('risk:leak', ('entity:event', {}), { 'template': {'title': 'leak'}, 'interfaces': ( ('meta:reported', {}), - ('entity:action', {}), + ('risk:victimized', {}), ), 'display': { 'columns': ( - {'type': 'prop', 'opts': {'name': 'disclosed'}}, + {'type': 'prop', 'opts': {'name': 'time'}}, {'type': 'prop', 'opts': {'name': 'name'}}, - {'type': 'prop', 'opts': {'name': 'owner::name'}}, + {'type': 'prop', 'opts': {'name': 'victim::name'}}, {'type': 'prop', 'opts': {'name': 'reporter:name'}}, ), }, @@ -261,62 +271,94 @@ async def _normPyStr(self, text, view=None): ), 'doc': 'A hierarchical taxonomy of leak event types.'}), - ('risk:extortion', ('guid', {}), { + ('risk:extortion', ('entity:activity', {}), { 'template': {'title': 'extortion'}, 'interfaces': ( ('meta:reported', {}), - ('entity:action', {}), + ('biz:negotiable', {}), + ('risk:victimized', {}), + ), + 'props': ( + ('type', ('risk:extortion:type:taxonomy', {}), { + 'doc': 'A type taxonomy for the extortion event.'}), + + ('success', ('bool', {}), { + 'doc': "Set to true if the victim met the attacker's demands."}), + + ('demand', ('biz:asked', {}), { + 'doc': 'The initial demand made by the actor.'}), + + ('enacted', ('bool', {}), { + 'doc': 'Set to true if attacker carried out the threat.'}), + + ('public', ('bool', {}), { + 'doc': 'Set to true if the attacker publicly announced the extortion.'}), + + ('public:url', ('inet:url', {}), { + 'doc': 'The URL where the attacker publicly announced the extortion.'}), + + ('paid:price', ('econ:price', {}), { + 'doc': 'The total price paid by the target of the extortion.'}), ), 'display': { 'columns': ( - {'type': 'prop', 'opts': {'name': 'demanded'}}, {'type': 'prop', 'opts': {'name': 'name'}}, - {'type': 'prop', 'opts': {'name': 'target::name'}}, + {'type': 'prop', 'opts': {'name': 'victim::name'}}, {'type': 'prop', 'opts': {'name': 'reporter:name'}}, - {'type': 'prop', 'opts': {'name': 'deadline'}}, ), }, 'doc': 'An event where an attacker attempted to extort a victim.'}), - ('risk:extortion:status:taxonomy', ('taxonomy', {}), { + ('risk:theft', ('meta:event', {}), { 'interfaces': ( - ('meta:taxonomy', {}), + ('risk:victimized', {}), ), - 'doc': 'A hierarchical taxonomy of extortion statuses.'}), + 'props': (), + }), ('risk:outage:cause:taxonomy', ('taxonomy', {}), { 'interfaces': ( ('meta:taxonomy', {}), ), + 'props': (), 'doc': 'An outage cause taxonomy.'}), ('risk:outage:type:taxonomy', ('taxonomy', {}), { 'interfaces': ( ('meta:taxonomy', {}), ), + 'props': (), 'doc': 'An outage type taxonomy.'}), - ('risk:outage:status:taxonomy', ('taxonomy', {}), { - 'interfaces': ( - ('meta:taxonomy', {}), - ), - 'doc': 'An outage status taxonomy.'}), - - ('risk:outage', ('guid', {}), { + ('risk:outage', ('meta:activity', {}), { 'template': {'title': 'outage'}, 'interfaces': ( ('meta:reported', {}), ), 'display': { 'columns': ( - {'type': 'prop', 'opts': {'name': 'period'}}, {'type': 'prop', 'opts': {'name': 'name'}}, - {'type': 'prop', 'opts': {'name': 'provider:name'}}, + {'type': 'prop', 'opts': {'name': 'period'}}, {'type': 'prop', 'opts': {'name': 'reporter:name'}}, {'type': 'prop', 'opts': {'name': 'cause'}}, {'type': 'prop', 'opts': {'name': 'type'}}, ), }, + 'props': ( + ('provider', ('ou:org', {}), { + 'doc': 'The provider which experienced the outage.'}), + + ('provider:name', ('entity:name', {}), { + 'doc': 'The name of the provider which experienced the outage.'}), + + ('type', ('risk:outage:type:taxonomy', {}), { + 'ex': 'service.power', + 'doc': 'The type of outage.'}), + + ('cause', ('risk:outage:cause:taxonomy', {}), { + 'ex': 'nature.earthquake', + 'doc': 'The outage cause type.'}), + ), 'doc': 'An outage event which affected resource availability.'}), ('risk:extortion:type:taxonomy', ('taxonomy', {}), { @@ -330,13 +372,22 @@ async def _normPyStr(self, text, view=None): ), 'interfaces': ( + ('risk:victimized', { + 'props': ( + ('victim', ('entity:actor', {}), { + 'doc': 'The victim of the {title}.'}), + + ('victim:name', ('entity:name', {}), { + 'doc': 'The name of the victim of the {title}.'}), + ), + # TODO add similar context to props above + 'doc': 'An interface for malicious acts which directly impact victim.'}), + ('risk:mitigatable', { - 'doc': 'A common interface for risks which may be mitigated.', - }), + 'doc': 'A common interface for risks which may be mitigated.'}), ('risk:targetable', { - 'doc': 'A common interface for nodes which may target selection criteria for threats.', - }), + 'doc': 'A common interface for nodes which may target selection criteria for threats.'}), ), 'edges': ( # some explicit examples... @@ -347,29 +398,33 @@ async def _normPyStr(self, text, view=None): (('entity:action', 'targeted', 'risk:targetable'), { 'doc': 'The action represents the actor targeting based on the target node.'}), - (('risk:compromise', 'stole', 'meta:observable'), { - 'doc': 'The target node was stolen or copied as a result of the compromise.'}), + (('risk:theft', 'stole', 'meta:observable'), { + 'doc': 'The target node was stolen or copied as part of the theft.'}), + + (('risk:theft', 'stole', 'phys:object'), { + 'doc': 'The target node was stolen as a part of the theft.'}), + + (('risk:compromise', 'ledto', 'risk:leak'), { + 'doc': 'The compromise led to the leak.'}), - (('risk:compromise', 'stole', 'phys:object'), { - 'doc': 'The target node was stolen as a result of the compromise.'}), + (('risk:compromise', 'ledto', 'risk:extortion'), { + 'doc': 'The compromise led to the leak.'}), (('risk:leak', 'leaked', 'meta:observable'), { 'doc': 'The leak included the disclosure of the target node.'}), - (('risk:leak', 'enabled', 'risk:leak'), { - 'doc': 'The source leak enabled the target leak to occur.'}), + (('risk:leak', 'ledto', 'risk:leak'), { + 'doc': 'The source leak lead to the target leak.'}), + # FIXME should this be removed in favor of risk:leak -(ledto)> risk:extortion? (('risk:extortion', 'leveraged', 'meta:observable'), { '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:extortion', 'ledto', 'econ:payment'), { + 'doc': 'The extortion lead to the party making the payment to the actor.'}), - (('risk:attack', 'caused', 'risk:alert'), { - 'doc': 'The attack caused the alert.'}), - - (('risk:attack', 'caused', 'risk:outage'), { - 'doc': 'The attack caused the outage.'}), + (('risk:attack', 'ledto', 'risk:outage'), { + 'doc': 'The attack led to the outage.'}), (('risk:outage', 'impacted', None), { 'doc': 'The outage event impacted the availability of the target node.'}), @@ -642,19 +697,6 @@ async def _normPyStr(self, text, view=None): ('vector', ('risk:attack', {}), { 'doc': 'The attack assessed to be the initial compromise vector.'}), - ('target', ('entity:actor', {}), { - 'doc': 'Contact information representing the target.'}), - - ('period', ('ival', {}), { - 'doc': 'The period over which the target was compromised.'}), - - # FIXME - is this overfit being one-to-one? - ('campaign', ('entity:campaign', {}), { - 'doc': 'The campaign that this compromise is part of.'}), - - ('detected', ('time', {}), { - 'doc': 'The first confirmed detection time of the compromise.'}), - ('loss:pii', ('int', {}), { 'doc': 'The number of records compromised which contain PII.'}), @@ -691,66 +733,29 @@ async def _normPyStr(self, text, view=None): ('risk:attack:type:taxonomy', { 'prevnames': ('risk:attacktype',)}, ()), - ('risk:attack', {}, ( - - ('type', ('risk:attack:type:taxonomy', {}), { - 'ex': 'cno.phishing', - 'doc': 'A type for the attack, as a taxonomy entry.'}), - - ('time', ('time', {}), { - 'doc': 'Set if the time of the attack is known.'}), - - ('detected', ('time', {}), { - 'doc': 'The first confirmed detection time of the attack.'}), - - ('success', ('bool', {}), { - 'doc': 'Set if the attack was known to have succeeded or not.'}), - - # FIXME overfit - ('campaign', ('entity:campaign', {}), { - 'doc': 'Set if the attack was part of a larger campaign.'}), - - ('compromise', ('risk:compromise', {}), { - 'doc': 'A compromise that this attack contributed to.'}), - - ('severity', ('meta:score', {}), { - 'doc': 'A severity rank for the attack.'}), - - ('sophistication', ('meta:score', {}), { - 'doc': 'The assessed sophistication of the attack.'}), - - ('prev', ('risk:attack', {}), { - 'doc': 'The previous/parent attack in a list or hierarchy.'}), - - )), - ('risk:leak:type:taxonomy', {}, ()), ('risk:leak', {}, ( - ('disclosed', ('time', {}), { - 'doc': 'The time the leaked information was disclosed.'}), + # ('disclosed', ('time', {}), { + # 'doc': 'The time the leaked information was disclosed.'}), - ('owner', ('entity:actor', {}), { - 'doc': 'The owner of the leaked information.'}), + # ('owner', ('entity:actor', {}), { + # 'doc': 'The owner of the leaked information.'}), + # FIXME this form is conflated between the leak event an effects / relationships... + # should we make a sub-form of leak for disclosure to a specific entity? ('recipient', ('entity:actor', {}), { 'doc': 'The identity which received the leaked information.'}), - ('type', ('risk:leak:type:taxonomy', {}), { - 'doc': 'A type taxonomy for the leak.'}), - - ('compromise', ('risk:compromise', {}), { - 'doc': 'The compromise which allowed the leaker access to the information.'}), - - ('extortion', ('risk:extortion', {}), { - 'doc': 'The extortion event which used the threat of the leak as leverage.'}), + # ('type', ('risk:leak:type:taxonomy', {}), { + # 'doc': 'A type taxonomy for the leak.'}), ('public', ('bool', {}), { 'doc': 'Set to true if the leaked information was made publicly available.'}), - ('public:urls', ('array', {'type': 'inet:url'}), { + ('urls', ('array', {'type': 'inet:url'}), { 'prevnames': ('public:url',), - 'doc': 'The URL where the leaked information was made publicly available.'}), + 'doc': 'The URL where the actor published the information.'}), ('size:bytes', ('int', {'min': 0}), { 'doc': 'The total size of the leaked data in bytes.'}), @@ -759,76 +764,8 @@ async def _normPyStr(self, text, view=None): 'doc': 'The number of files included in the leaked data.'}), ('size:percent', ('int', {'min': 0, 'max': 100}), { - 'doc': 'The total percent of the data leaked.'}), - - )), - - ('risk:outage:type:taxonomy', {}, ()), - ('risk:outage:cause:taxonomy', {}, ()), - ('risk:outage', {}, ( - - ('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', {}), { - 'ex': 'nature.earthquake', - 'doc': 'The outage cause type.'}), - - ('attack', ('risk:attack', {}), { - 'doc': 'An attack which caused the outage.'}), - - ('provider', ('ou:org', {}), { - 'doc': 'The organization which experienced the outage event.'}), - - ('provider:name', ('entity:name', {}), { - 'doc': 'The name of the organization which experienced the outage event.'}), - )), - - ('risk:extortion:type:taxonomy', {}, ()), - ('risk:extortion', {}, ( - - ('demanded', ('time', {}), { - 'doc': 'The time that the attacker made their demands.'}), - - ('deadline', ('time', {}), { - 'doc': 'The time that the demand must be met.'}), - - ('type', ('risk:extortion:type:taxonomy', {}), { - 'doc': 'A type taxonomy for the extortion event.'}), - - ('target', ('entity:actor', {}), { - 'doc': 'The extortion target identity.'}), - - ('success', ('bool', {}), { - 'doc': "Set to true if the victim met the attacker's demands."}), - - ('enacted', ('bool', {}), { - 'doc': 'Set to true if attacker carried out the threat.'}), - - ('public', ('bool', {}), { - 'doc': 'Set to true if the attacker publicly announced the extortion.'}), - - ('public:url', ('inet:url', {}), { - 'doc': 'The URL where the attacker publicly announced the extortion.'}), - - ('compromise', ('risk:compromise', {}), { - 'doc': 'The compromise which allowed the attacker to extort the target.'}), - - ('demanded:payment:price', ('econ:price', {}), { - 'doc': 'The payment price which was demanded.'}), - - ('demanded:payment:currency', ('econ:currency', {}), { - 'doc': 'The currency in which payment was demanded.'}), - - ('paid:price', ('econ:price', {}), { - 'doc': 'The total price paid by the target of the extortion.'}), + 'doc': 'The total percent of the compromised data leaked.'}), - ('payments', ('array', {'type': 'econ:payment'}), { - 'doc': 'Payments made from the target to the attacker.'}), )), ), }), diff --git a/synapse/models/science.py b/synapse/models/science.py index b8eb9ecd14..44df3bb86b 100644 --- a/synapse/models/science.py +++ b/synapse/models/science.py @@ -8,6 +8,9 @@ 'doc': 'A taxonomy of hypothesis types.'}), ('sci:hypothesis', ('guid', {}), { + 'interfaces': ( + ('meta:believable', {}), + ), 'doc': 'A hypothesis or theory.'}), # TODO link experiment to eventual procedure node @@ -20,7 +23,15 @@ ('sci:experiment', ('guid', {}), { 'doc': 'An instance of running an experiment.'}), - ('sci:observation', ('guid', {}), { + ('sci:observation', ('entity:event', {}), { + 'template': {'title': 'observation'}, + 'props': ( + ('experiment', ('sci:experiment', {}), { + 'doc': 'The experiment which produced the observation.'}), + + ('desc', ('text', {}), { + 'doc': 'A description of the observation.'}), + ), 'doc': 'An observation which may have resulted from an experiment.'}), ('sci:evidence', ('guid', {}), { @@ -72,19 +83,6 @@ 'doc': 'The time period when the experiment was run.'}), )), - - ('sci:observation', {}, ( - - ('experiment', ('sci:experiment', {}), { - 'doc': 'The experiment which produced the observation.'}), - - ('desc', ('text', {}), { - 'doc': 'A description of the observation.'}), - - ('time', ('time', {}), { - 'doc': 'The time that the observation occurred.'}), - )), - ('sci:evidence', {}, ( ('hypothesis', ('sci:experiment', {}), { diff --git a/synapse/models/transport.py b/synapse/models/transport.py index b74e31b070..421f23ff81 100644 --- a/synapse/models/transport.py +++ b/synapse/models/transport.py @@ -25,7 +25,7 @@ ('transport:vehicle', ('ndef', {'interface': 'transport:vehicle'}), { 'doc': 'A vehicle such as an aircraft or sea vessel.'}), - ('transport:occupant', ('guid', {}), { + ('transport:occupant', ('entity:activity', {}), { 'doc': 'An occupant of a vehicle on a trip.'}), ('transport:occupant:role:taxonomy', ('taxonomy', {}), { @@ -214,7 +214,7 @@ # FIXME ownership interface? ('owner', ('entity:actor', {}), { - 'doc': 'The contact information of the owner of the {title}.'}), + 'doc': 'The actor who currently owns the {title}.'}), ), }), # most containers are vehicles, but some are not... @@ -328,8 +328,7 @@ ('id', ('meta:id', {}), { 'doc': 'The license ID.'}), - # TODO type ( drivers license, commercial trucking, etc? ) - ('contact', ('entity:actor', {}), { + ('contact', ('entity:contact', {}), { 'doc': 'The contact info of the licensee.'}), ('issued', ('time', {}), { @@ -542,9 +541,6 @@ ('role', ('transport:occupant:role:taxonomy', {}), { 'doc': 'The role of the occupant such as captain, crew, passenger.'}), - ('contact', ('entity:individual', {}), { - 'doc': 'Contact information of the occupant.'}), - ('trip', ('transport:trip', {}), { 'doc': 'The trip, such as a flight or train ride, being taken by the occupant.'}), @@ -555,7 +551,6 @@ 'doc': 'The seat which the occupant sat in. Likely in a vehicle specific format.'}), ('period', ('ival', {}), { - 'prevnames': ('boarded', 'disembarked'), 'doc': 'The period when the occupant was aboard the vehicle.'}), ('boarded:place', ('geo:place', {}), { diff --git a/synapse/tests/test_model_base.py b/synapse/tests/test_model_base.py index 9cc4980bd4..c59a35a949 100644 --- a/synapse/tests/test_model_base.py +++ b/synapse/tests/test_model_base.py @@ -13,7 +13,8 @@ async def test_model_base_timeline(self): self.len(1, nodes) nodes = await core.nodes('''[ meta:event=* :title=Hehe - :desc=Haha :period=(202203221400, 202203221600) + :desc=Haha + :time=202203221400 :type=hehe.haha <(has)+ {meta:timeline:title=Woot} +(about)> {[ inet:fqdn=vertex.link ]} @@ -26,7 +27,6 @@ async def test_model_base_timeline(self): self.len(1, await core.nodes('meta:event -(about)> inet:fqdn')) self.len(1, await core.nodes('meta:event <(has)- meta:timeline')) self.len(1, await core.nodes('meta:event -> meta:event:type:taxonomy')) - self.len(1, await core.nodes('meta:event +:title=Hehe +:desc=Haha +:period.duration=2:00:00 +:type=hehe.haha')) async def test_model_base_meta_taxonomy(self): async with self.getTestCore() as core: diff --git a/synapse/tests/test_model_belief.py b/synapse/tests/test_model_belief.py index a802488ee6..80b25a8d03 100644 --- a/synapse/tests/test_model_belief.py +++ b/synapse/tests/test_model_belief.py @@ -27,16 +27,13 @@ async def test_model_belief(self): self.len(2, await core.nodes('belief:system -(has)> belief:tenet +:desc=Lol')) nodes = await core.nodes('''[ - belief:subscriber=* - :contact={[ entity:contact=* :name=visi ]} - :system={ belief:system:type=hehe.haha } + entity:believed=* + :actor={[ entity:contact=* :name=visi ]} + :belief={ belief:system:type=hehe.haha } :period=(20230209, 20230210) - +(follows)> { belief:tenet:name="zip zop" } ]''') self.len(1, nodes) - self.nn(nodes[0].get('system')) - self.nn(nodes[0].get('contact')) + self.nn(nodes[0].get('actor')) + self.nn(nodes[0].get('belief')) self.propeq(nodes[0], 'period', (1675900800000000, 1675987200000000, 86400000000)) - - self.len(1, await core.nodes('belief:subscriber -(follows)> belief:tenet')) diff --git a/synapse/tests/test_model_doc.py b/synapse/tests/test_model_doc.py index f533c3e6e8..a99728de42 100644 --- a/synapse/tests/test_model_doc.py +++ b/synapse/tests/test_model_doc.py @@ -76,8 +76,8 @@ async def test_model_doc(self): :desc="Thought leader seeks..." :skills={[ ps:skill=* ]} :workhist={[ ps:workhist=* ]} - :education={[ ps:education=* ]} - :achievements={[ ps:achievement=* ]} + :education={[ entity:educated=* ]} + :achievements={[ entity:awarded=* ]} ] ''') self.eq('V-99', nodes[0].get('id')) @@ -91,8 +91,8 @@ async def test_model_doc(self): self.len(1, await core.nodes('doc:resume :skills -> ps:skill')) self.len(1, await core.nodes('doc:resume :contact -> entity:contact')) self.len(1, await core.nodes('doc:resume :workhist -> ps:workhist')) - self.len(1, await core.nodes('doc:resume :education -> ps:education')) - self.len(1, await core.nodes('doc:resume :achievements -> ps:achievement')) + self.len(1, await core.nodes('doc:resume :education -> entity:educated')) + self.len(1, await core.nodes('doc:resume :achievements -> entity:awarded')) nodes = await core.nodes(''' [ doc:contract=* @@ -100,7 +100,6 @@ async def test_model_doc(self): :type=foo.bar :issuer={[ ou:org=({"name": "vertex"}) ]} :parties={[ entity:contact=* entity:contact=* ]} - :signers={[ entity:contact=* entity:contact=* ]} :file={[ file:bytes=* ]} :signed=202001 :period=(202002, 202003) @@ -118,7 +117,16 @@ async def test_model_doc(self): self.len(1, await core.nodes('doc:contract :issuer -> ou:org')) self.len(2, await core.nodes('doc:contract :parties -> *')) - self.len(2, await core.nodes('doc:contract :signers -> *')) + + nodes = await core.nodes('''[ + entity:signed=* + :document={ doc:contract } + :actor={[ syn:user=root ]} + :time=20260226 + ]''') + self.nn(nodes[0].get('actor')) + self.nn(nodes[0].get('document')) + self.eq(nodes[0].get('time'), 1772064000000000) nodes = await core.nodes('doc:contract -> doc:contract:type:taxonomy') self.len(1, nodes) diff --git a/synapse/tests/test_model_entity.py b/synapse/tests/test_model_entity.py index dcb63011e1..b2ddb80e2a 100644 --- a/synapse/tests/test_model_entity.py +++ b/synapse/tests/test_model_entity.py @@ -67,21 +67,17 @@ async def test_model_entity(self): self.eq(nodes[0].ndef, ndef) nodes = await core.nodes('''[ - entity:attendee=* - :person={[ ps:person=* ]} + entity:attended=* + :actor={[ ps:person=* ]} :period=(201202,201203) - :event={[ ou:event=* ]} - :roles+=staff - :roles+=STAFF + :event={[ ou:meeting=* ]} ]''') self.len(1, nodes) - self.eq(('staff',), nodes[0].get('roles')) + # FIXME discuss + # self.eq(('staff',), nodes[0].get('roles')) self.eq(nodes[0].get('period'), (1328054400000000, 1330560000000000, 2505600000000)) - - self.len(1, await core.nodes('entity:attendee -> ps:person')) - - self.len(1, await core.nodes('entity:attendee -> ou:event')) - self.len(1, await core.nodes('entity:attendee :event -> ou:event')) + self.len(1, await core.nodes('entity:attended :actor -> ps:person')) + self.len(1, await core.nodes('entity:attended :event -> ou:meeting')) nodes = await core.nodes(''' [ entity:campaign=* @@ -98,7 +94,6 @@ async def test_model_entity(self): :reporter={[ ou:org=({"name": "vertex"}) ]} :reporter:name=vertex :actor={[ entity:contact=* ]} - :actors={[ entity:contact=* ]} +(had)> {[ entity:goal=* ]} ] ''') @@ -160,18 +155,16 @@ async def test_model_entity(self): nodes = await core.nodes(''' [ entity:contribution=* - :actor={[ ou:org=* :name=vertex ]} + :actor={[ ou:org=({"name": "vertex"}) ]} :time=20220718 :value=10 - :currency=usd - :campaign={[ entity:campaign=({"name": "good guys"}) ]} + :event={[ entity:campaign=({"name": "good guys"}) ]} ] ''') self.eq(1658102400000000, nodes[0].get('time')) self.eq('10', nodes[0].get('value')) - self.eq('usd', nodes[0].get('currency')) - self.len(1, await core.nodes('entity:contribution -> entity:campaign')) - self.len(1, await core.nodes('entity:contribution -> ou:org +:name=vertex')) + self.len(1, await core.nodes('entity:contribution :event -> entity:campaign')) + self.len(1, await core.nodes('entity:contribution :actor -> ou:org +:name=vertex')) nodes = await core.nodes(''' [ entity:conflict=* @@ -182,7 +175,7 @@ async def test_model_entity(self): self.eq(nodes[0].get('name'), 'world war iii') self.eq(nodes[0].get('period'), (2493072000000000, 2493072000000001, 1)) - nodes = await core.nodes('[ entity:campaign=* :name="good guys" :names=("pacific campaign",) :conflict={entity:conflict} ]') + nodes = await core.nodes('[ entity:campaign=* :name="good guys" :names=("pacific campaign",) :activity={entity:conflict} ]') self.len(1, await core.nodes('entity:campaign -> entity:conflict')) self.len(1, await core.nodes('entity:campaign:names*[="pacific campaign"]')) @@ -198,6 +191,16 @@ async def test_model_entity(self): self.len(1, await core.nodes('entity:contactlist :source -> it:host')) self.len(2, await core.nodes('entity:contactlist -(has)> entity:contact')) + nodes = await core.nodes('''[ + entity:believed=* + :actor={[ syn:user=root ]} + :belief={[ belief:system=* :name=aliens ]} + :period=2049* + ]''') + self.eq(nodes[0].get('period'), (2493072000000000, 2493072000000001, 1)) + self.len(1, await core.nodes('entity:believed :actor -> syn:user')) + self.len(1, await core.nodes('entity:believed :belief -> belief:system')) + async def test_entity_relationship(self): async with self.getTestCore() as core: diff --git a/synapse/tests/test_model_geopol.py b/synapse/tests/test_model_geopol.py index ce0ce53355..85af03962d 100644 --- a/synapse/tests/test_model_geopol.py +++ b/synapse/tests/test_model_geopol.py @@ -112,10 +112,10 @@ async def test_model_geopol_election(self): self.len(1, await core.nodes('pol:race -> pol:election +:time=20241103')) nodes = await core.nodes(''' - [ pol:candidate=* + [ pol:candidacy=* :id=" P00009423" :race={pol:race} - :contact={[entity:contact=* :name=whippit]} + :actor={[entity:contact=* :name=whippit]} :winner=$lib.true :campaign={[entity:campaign=* :name=whippit4prez ]} :party={[ou:org=* :name=vertex]} @@ -123,15 +123,15 @@ async def test_model_geopol_election(self): ''') self.eq(1, nodes[0].get('winner')) self.eq('P00009423', nodes[0].get('id')) - self.len(1, await core.nodes('pol:candidate -> pol:race')) - self.len(1, await core.nodes('pol:candidate -> ou:org +:name=vertex')) - self.len(1, await core.nodes('pol:candidate -> entity:contact +:name=whippit')) - self.len(1, await core.nodes('pol:candidate -> entity:campaign +:name=whippit4prez')) + self.len(1, await core.nodes('pol:candidacy -> pol:race')) + self.len(1, await core.nodes('pol:candidacy -> ou:org +:name=vertex')) + self.len(1, await core.nodes('pol:candidacy -> entity:contact +:name=whippit')) + self.len(1, await core.nodes('pol:candidacy -> entity:campaign +:name=whippit4prez')) nodes = await core.nodes(''' [ pol:term=* :office={pol:office:title=potus} - :contact={entity:contact:name=whippit} + :actor={entity:contact:name=whippit} :race={pol:race} :party={ou:org:name=vertex} :period=(20250120, 20290120) diff --git a/synapse/tests/test_model_orgs.py b/synapse/tests/test_model_orgs.py index 48d1487c9f..c5256e4a7c 100644 --- a/synapse/tests/test_model_orgs.py +++ b/synapse/tests/test_model_orgs.py @@ -155,8 +155,7 @@ async def test_ou_simple(self): nodes = await core.nodes('''[ ou:conference=39f8d9599cd663b00013bfedf69dcf53 :name="arrowcon 2018" - :name:base=arrowcon - :names=("arrow conference 2018", "arrcon18", "arrcon18") + :family=arrowcon :period=(20180301, 20180303) :website=http://arrowcon.org/2018 :place=39f8d9599cd663b00013bfedf69dcf53 @@ -164,8 +163,7 @@ async def test_ou_simple(self): self.len(1, nodes) self.eq(nodes[0].ndef, ('ou:conference', '39f8d9599cd663b00013bfedf69dcf53')) self.eq(nodes[0].get('name'), 'arrowcon 2018') - self.eq(nodes[0].get('names'), ('arrcon18', 'arrow conference 2018',)) - self.eq(nodes[0].get('name:base'), 'arrowcon') + self.eq(nodes[0].get('family'), 'arrowcon') self.eq(nodes[0].get('period'), (1519862400000000, 1520035200000000, 172800000000)) self.eq(nodes[0].get('place'), '39f8d9599cd663b00013bfedf69dcf53') self.eq(nodes[0].get('website'), 'http://arrowcon.org/2018') @@ -174,16 +172,13 @@ async def test_ou_simple(self): self.eq(core.model.prop('ou:conference:place:address').info['doc'], 'The postal address where the conference was located.') - gutors = await core.nodes('[ ou:conference=({"name": "arrcon18"}) ]') - self.eq(nodes[0].ndef, gutors[0].ndef) - # ou:event nodes = await core.nodes('''[ ou:event=39f8d9599cd663b00013bfedf69dcf53 :name='arrowcon 2018 dinner' :desc='arrowcon dinner' :period=(201803011900, 201803012200) - :parent=(ou:conference, 39f8d9599cd663b00013bfedf69dcf53) + :activity={[ ou:conference=39f8d9599cd663b00013bfedf69dcf53 ]} :place=39f8d9599cd663b00013bfedf69dcf53 :website=http://arrowcon.org/2018/dinner ]''') @@ -191,7 +186,7 @@ async def test_ou_simple(self): self.eq(nodes[0].ndef, ('ou:event', '39f8d9599cd663b00013bfedf69dcf53')) self.eq(nodes[0].get('name'), 'arrowcon 2018 dinner') self.eq(nodes[0].get('desc'), 'arrowcon dinner') - self.eq(nodes[0].get('parent'), ('ou:conference', '39f8d9599cd663b00013bfedf69dcf53')) + self.eq(nodes[0].get('activity'), '39f8d9599cd663b00013bfedf69dcf53') self.eq(nodes[0].get('period'), (1519930800000000, 1519941600000000, 10800000000)) self.eq(nodes[0].get('place'), '39f8d9599cd663b00013bfedf69dcf53') self.eq(nodes[0].get('website'), 'http://arrowcon.org/2018/dinner') @@ -243,10 +238,10 @@ async def test_ou_simple(self): :place=* :place:loc=us.nv.lasvegas - :parent={ ou:conference } - :sponsors={[ entity:contact=* ]} - :organizers={[ entity:contact=* ]} - :presenters={[ entity:contact=* entity:contact=* ]} + :activity={ ou:conference } + //:sponsors={[ entity:contact=* ]} + //:organizers={[ entity:contact=* ]} + //:presenters={[ entity:contact=* entity:contact=* ]} :deck:file=* :recording:file=* @@ -272,15 +267,16 @@ async def test_ou_simple(self): self.eq(nodes[0].get('place:loc'), 'us.nv.lasvegas') self.len(1, await core.nodes(f'ou:preso -> ou:conference')) - self.len(1, await core.nodes(f'ou:preso :sponsors -> entity:contact')) - self.len(1, await core.nodes(f'ou:preso :organizers -> entity:contact')) - self.len(2, await core.nodes(f'ou:preso :presenters -> entity:contact')) + # FIXME resolve + # self.len(1, await core.nodes(f'ou:preso :sponsors -> entity:contact')) + # self.len(1, await core.nodes(f'ou:preso :organizers -> entity:contact')) + # self.len(2, await core.nodes(f'ou:preso :presenters -> entity:contact')) nodes = await core.nodes('''[ ou:contest=* :name="defcon ctf 2020" :type=cyber.ctf - :name:base="defcon ctf" + :family="defcon ctf" :period=(20200808, 20200811) :website=http://vertex.link/contest @@ -288,15 +284,15 @@ async def test_ou_simple(self): :place:latlong=(20, 30) :place:loc=us.nv.lasvegas - :parent={ ou:conference } - :organizers={[ entity:contact=* ]} - :sponsors={[ entity:contact=* ]} + :activity={ ou:conference } + //:organizers={[ entity:contact=* ]} + //:sponsors={[ entity:contact=* ]} ]''') self.len(1, nodes) self.eq('defcon ctf 2020', nodes[0].get('name')) self.eq('cyber.ctf.', nodes[0].get('type')) - self.eq('defcon ctf', nodes[0].get('name:base')) + self.eq('defcon ctf', nodes[0].get('family')) self.eq(nodes[0].get('period'), (1596844800000000, 1597104000000000, 259200000000)) @@ -306,28 +302,29 @@ async def test_ou_simple(self): self.eq('us.nv.lasvegas', nodes[0].get('place:loc')) self.len(1, await core.nodes(f'ou:contest -> ou:conference')) - self.len(1, await core.nodes(f'ou:contest :parent -> ou:conference')) - self.len(1, await core.nodes(f'ou:contest :sponsors -> entity:contact')) - self.len(1, await core.nodes(f'ou:contest :organizers -> entity:contact')) + self.len(1, await core.nodes(f'ou:contest :activity -> ou:conference')) + # FIXME resolve + # self.len(1, await core.nodes(f'ou:contest :sponsors -> entity:contact')) + # self.len(1, await core.nodes(f'ou:contest :organizers -> entity:contact')) nodes = await core.nodes('''[ - ou:contest:result=(*, *) + entity:competed=* :rank=1 :score=20 :url=https://vertex.link/woot :period=(20250101, 20250102) - :contest={ou:contest} - :participant={[ entity:contact=* ]} + :activity={ou:contest} + :actor={[ entity:contact=* ]} ]''') self.len(1, nodes) - self.nn(nodes[0].get('contest')) - self.nn(nodes[0].get('participant')) + self.nn(nodes[0].get('actor')) + self.nn(nodes[0].get('activity')) self.eq(nodes[0].get('url'), 'https://vertex.link/woot') self.eq(1, nodes[0].get('rank')) self.eq(20, nodes[0].get('score')) self.eq((1735689600000000, 1735776000000000, 86400000000), nodes[0].get('period')) - self.len(1, await core.nodes('ou:contest:result -> ou:contest')) - self.len(1, await core.nodes('ou:contest:result -> entity:contact')) + self.len(1, await core.nodes('entity:competed -> ou:contest')) + self.len(1, await core.nodes('entity:competed -> entity:contact')) opts = {'vars': {'ind': s_common.guid()}} nodes = await core.nodes('[ ou:org=* :industries=($ind, $ind) ]', opts=opts) diff --git a/synapse/tests/test_model_person.py b/synapse/tests/test_model_person.py index c76e75b59b..da0b8e2dbd 100644 --- a/synapse/tests/test_model_person.py +++ b/synapse/tests/test_model_person.py @@ -31,31 +31,23 @@ async def test_ps_simple(self): self.len(1, await core.nodes('ps:person :photo -> file:bytes')) nodes = await core.nodes('''[ - ps:achievement=* - :award=* - :awardee={[ entity:contact=* ]} - :awarded=20200202 - :expires=20210202 - :revoked=20201130 + entity:awarded=* + :award={[ meta:award=* ]} + :actor={[ entity:contact=* ]} + :time=20200202 + //:expires=20210202 + //:revoked=20201130 ]''') self.len(1, nodes) achv = nodes[0].ndef[1] - nodes = await core.nodes(''' - ou:award [ :name="Bachelors of Science" :type=degree :org=* ] - ''') - self.nn(nodes[0].get('org')) - self.eq('bachelors of science', nodes[0].get('name')) - self.eq('degree.', nodes[0].get('type')) - opts = {'vars': {'achv': achv}} nodes = await core.nodes('''[ - ps:education=* - :student={[ entity:contact=* ]} - :institution={[ entity:contact=* ]} + entity:educated=* + :actor={[ entity:contact=* ]} + :institution={[ ou:org=* ]} :period=(20200202, 20210202) - :achievement = $achv - + +(ledto)> { entity:awarded } +(included)> {[ edu:class=* ]} ]''', opts=opts) @@ -93,11 +85,10 @@ async def test_ps_simple(self): nodes = await core.nodes('''[ ps:workhist = * - :org = * - :org:name = WootCorp - :org:fqdn = wootwoot.com + :employer = {[ ou:org=* :name=WootCorp ]} + :employer:name = WootCorp :desc = "Wooting." - :contact = {[ entity:contact=* ]} + :actor = {[ entity:contact=* ]} :job:type = it.dev :employment:type = fulltime.salary :title = "Python Developer" @@ -106,8 +97,7 @@ async def test_ps_simple(self): :pay:currency = usd ]''') self.len(1, nodes) - self.eq(nodes[0].get('org:name'), 'wootcorp') - self.eq(nodes[0].get('org:fqdn'), 'wootwoot.com') + self.eq(nodes[0].get('employer:name'), 'wootcorp') self.eq(nodes[0].get('desc'), 'Wooting.') self.eq(nodes[0].get('job:type'), 'it.dev.') self.eq(nodes[0].get('employment:type'), 'fulltime.salary.') @@ -116,8 +106,8 @@ async def test_ps_simple(self): self.eq(nodes[0].get('pay'), '200000') self.eq(nodes[0].get('pay:currency'), 'usd') - self.nn(nodes[0].get('org')) - self.nn(nodes[0].get('contact')) + self.nn(nodes[0].get('actor')) + self.nn(nodes[0].get('employer')) self.len(1, await core.nodes('ps:workhist -> ou:org')) self.len(1, await core.nodes('ps:workhist -> entity:title')) diff --git a/synapse/tests/test_model_risk.py b/synapse/tests/test_model_risk.py index ad4ec0e88f..eb096d385e 100644 --- a/synapse/tests/test_model_risk.py +++ b/synapse/tests/test_model_risk.py @@ -19,13 +19,12 @@ async def test_model_risk(self): :reporter={[ entity:contact=* ]} :reporter:name=vertex :time=20200202 - :detected = 20210203 + :discovered = 20210203 :success=true :type=foo.bar :severity=10 :desc=wootwoot - :campaign=* - :prev=* + :activity={[ entity:campaign=* ]} :actor = {[ entity:contact=* ]} :sophistication=high :url=https://vertex.link/attacks/CASE-2022-03 @@ -34,7 +33,7 @@ async def test_model_risk(self): ]''') self.eq(nodes[0].ndef, ('risk:attack', '17eb16247855525d6f9cb1585a59877f')) self.eq(nodes[0].get('time'), 1580601600000000) - self.eq(nodes[0].get('detected'), 1612310400000000) + self.eq(nodes[0].get('discovered'), 1612310400000000) self.eq(nodes[0].get('desc'), 'wootwoot') self.eq(nodes[0].get('type'), 'foo.bar.') self.eq(nodes[0].get('success'), True) @@ -48,9 +47,8 @@ async def test_model_risk(self): self.len(1, await core.nodes('risk:attack -(had)> entity:goal')) self.len(1, await core.nodes('risk:attack -> risk:attack:type:taxonomy')) - self.len(1, await core.nodes('risk:attack=17eb16247855525d6f9cb1585a59877f -> entity:campaign')) - self.len(1, await core.nodes('risk:attack=17eb16247855525d6f9cb1585a59877f :prev -> risk:attack')) self.len(1, await core.nodes('risk:attack=17eb16247855525d6f9cb1585a59877f :actor -> entity:contact')) + self.len(1, await core.nodes('risk:attack=17eb16247855525d6f9cb1585a59877f :activity -> entity:campaign')) nodes = await core.nodes('''[ risk:vuln=17eb16247855525d6f9cb1585a59877f @@ -160,7 +158,6 @@ async def test_model_risk(self): self.eq(nodes[0].get('cvss:v3_1:score:temporal'), 3.2) self.eq(nodes[0].get('cvss:v3_1:score:environmental'), 3.3) - self.len(2, await core.nodes('risk:vuln:id=VISI-0000 -> meta:id')) self.len(1, await core.nodes('risk:vuln:id=VISI-0000 :discoverer -> entity:contact')) self.len(1, await core.nodes('risk:vuln:cve=CVE-2013-0000 -> it:sec:cve')) @@ -237,11 +234,11 @@ async def test_model_risk(self): :reporter = {[ ou:org=({"name": "vertex"}) ]} :reporter:name = vertex :severity = 10 - :target = {[ entity:contact=* :name=ledo ]} + :victim = {[ entity:contact=* :name=ledo ]} :actor = {[ entity:contact=* :name=visi ]} - :campaign = * + :activity = {[ entity:campaign=* ]} :period = (20210202, 20210204) - :detected = 20210203 + :discovered = 20210203 :loss:pii = 400 :loss:econ = 1337 :loss:life = 0 @@ -260,12 +257,12 @@ async def test_model_risk(self): self.eq('vertex', nodes[0].get('reporter:name')) self.eq('PWN-00', nodes[0].get('id')) self.eq('https://vertex.link/pwned', nodes[0].get('url')) - self.nn(nodes[0].get('target')) + self.nn(nodes[0].get('victim')) self.nn(nodes[0].get('actor')) - self.nn(nodes[0].get('campaign')) + self.nn(nodes[0].get('activity')) self.nn(nodes[0].get('reporter')) self.eq(nodes[0].get('period'), (1612224000000000, 1612396800000000, 172800000000)) - self.eq(1612310400000000, nodes[0].get('detected')) + self.eq(1612310400000000, nodes[0].get('discovered')) self.eq(400, nodes[0].get('loss:pii')) self.eq('1337', nodes[0].get('loss:econ')) self.eq(0, nodes[0].get('loss:life')) @@ -277,10 +274,10 @@ async def test_model_risk(self): self.eq('usd', nodes[0].get('econ:currency')) self.eq(10, nodes[0].get('severity')) self.len(1, await core.nodes('risk:compromise -> syn:tag')) - self.len(1, await core.nodes('risk:compromise -> entity:campaign')) self.len(1, await core.nodes('risk:compromise -> risk:compromise:type:taxonomy')) self.len(1, await core.nodes('risk:compromise :vector -> risk:attack')) - self.len(1, await core.nodes('risk:compromise :target -> entity:contact +:name=ledo')) + self.len(1, await core.nodes('risk:compromise :activity -> entity:campaign')) + self.len(1, await core.nodes('risk:compromise :victim -> entity:contact +:name=ledo')) self.len(1, await core.nodes('risk:compromise :actor -> entity:contact +:name=visi')) nodes = await core.nodes(''' @@ -340,83 +337,79 @@ async def test_model_risk(self): nodes = await core.nodes('''[ risk:leak=* :name="WikiLeaks ACME Leak" :desc="WikiLeaks leaked ACME stuff." - :disclosed=20231102 - :owner={ gen.ou.org acme } + :time=20231102 + :victim={ gen.ou.org acme } :actor={ gen.ou.org wikileaks } :recipient={ gen.ou.org everyone } - :type=public - :compromise={[ risk:compromise=* :target={ gen.ou.org acme } ]} + //:type=public + //:compromise={[ risk:compromise=* :victim={ gen.ou.org acme } ]} :public=(true) - :public:urls=(https://wikileaks.org/acme,) + :urls=(https://wikileaks.org/acme,) :reporter={ gen.ou.org vertex } :reporter:name=vertex :size:bytes=99 :size:count=33 :size:percent=12 - :extortion=* + //:extortion=* + // FIXME this should be folded into new entity:motiv stuff +(had)> {[ entity:goal=({"name": "publicity"}) ]} ]''') self.len(1, nodes) self.eq('wikileaks acme leak', nodes[0].get('name')) self.eq('WikiLeaks leaked ACME stuff.', nodes[0].get('desc')) - self.eq(1698883200000000, nodes[0].get('disclosed')) - self.eq('public.', nodes[0].get('type')) + self.eq(nodes[0].get('time'), 1698883200000000) + # FIXME resolve... + # self.eq('public.', nodes[0].get('type')) self.eq(1, nodes[0].get('public')) self.eq(99, nodes[0].get('size:bytes')) self.eq(33, nodes[0].get('size:count')) self.eq(12, nodes[0].get('size:percent')) - self.eq(('https://wikileaks.org/acme',), nodes[0].get('public:urls')) + self.eq(('https://wikileaks.org/acme',), nodes[0].get('urls')) self.eq('vertex', nodes[0].get('reporter:name')) - self.len(1, await core.nodes('risk:leak -> risk:extortion')) - self.len(1, await core.nodes('risk:leak -> risk:leak:type:taxonomy')) - self.len(1, await core.nodes('risk:leak :owner -> ou:org +:name=acme')) + # self.len(1, await core.nodes('risk:leak -> risk:extortion')) + # self.len(1, await core.nodes('risk:leak -> risk:leak:type:taxonomy')) + self.len(1, await core.nodes('risk:leak :victim -> ou:org +:name=acme')) self.len(1, await core.nodes('risk:leak :actor -> ou:org +:name=wikileaks')) self.len(1, await core.nodes('risk:leak :recipient -> ou:org +:name=everyone')) - self.len(1, await core.nodes('risk:leak -(had)> entity:goal +:name=publicity')) - self.len(1, await core.nodes('risk:leak -> risk:compromise :target -> ou:org +:name=acme')) + # self.len(1, await core.nodes('risk:leak -(had)> entity:goal +:name=publicity')) + # self.len(1, await core.nodes('risk:leak -> risk:compromise :victim -> ou:org +:name=acme')) self.len(1, await core.nodes('risk:leak :reporter -> ou:org +:name=vertex')) nodes = await core.nodes('''[ risk:extortion=* - :demanded=20231102 - :deadline=20240329 + :demand={[ econ:asked=* :price=99.99 :time=20231102 :expires=20240329 ]} :name="APT99 Extorted ACME" :desc="APT99 extorted ACME for a zillion vertex coins." :type=fingain :actor={[ entity:contact=* :name=agent99 ]} - :target={ gen.ou.org acme } + :victim={ gen.ou.org acme } :success=(true) :enacted=(true) :public=(true) :public:url=https://apt99.com/acme - :compromise={[ risk:compromise=* :target={ gen.ou.org acme } ]} - :demanded:payment:price=99.99 - :demanded:payment:currency=VTC :reporter={ gen.ou.org vertex } :reporter:name=vertex :paid:price=12345 - :payments={[ econ:payment=* ]} + +(ledto)> {[ econ:payment=* ]} + <(ledto)+ {[ risk:compromise=* :victim={ gen.ou.org acme } ]} ]''') self.len(1, nodes) self.eq('apt99 extorted acme', nodes[0].get('name')) self.eq('APT99 extorted ACME for a zillion vertex coins.', nodes[0].get('desc')) - self.eq(1698883200000000, nodes[0].get('demanded')) - self.eq(1711670400000000, nodes[0].get('deadline')) - self.eq('fingain.', nodes[0].get('type')) + # FIXME resolve + # self.eq('fingain.', nodes[0].get('type')) self.eq(1, nodes[0].get('public')) self.eq(1, nodes[0].get('success')) self.eq(1, nodes[0].get('enacted')) self.eq('https://apt99.com/acme', nodes[0].get('public:url')) - self.eq('99.99', nodes[0].get('demanded:payment:price')) - self.eq('vtc', nodes[0].get('demanded:payment:currency')) self.eq('vertex', nodes[0].get('reporter:name')) self.eq('12345', nodes[0].get('paid:price')) - self.len(1, await core.nodes('risk:extortion -> econ:payment')) - self.len(1, await core.nodes('risk:extortion :target -> ou:org +:name=acme')) + self.len(1, await core.nodes('risk:extortion -(ledto)> econ:payment')) + self.len(1, await core.nodes('risk:extortion :victim -> ou:org +:name=acme')) self.len(1, await core.nodes('risk:extortion :actor -> entity:contact +:name=agent99')) - self.len(1, await core.nodes('risk:extortion -> risk:compromise :target -> ou:org +:name=acme')) + self.len(1, await core.nodes('risk:extortion <(ledto)- risk:compromise :victim -> ou:org +:name=acme')) self.len(1, await core.nodes('risk:extortion :reporter -> ou:org +:name=vertex')) nodes = await core.nodes(''' @@ -454,13 +447,12 @@ async def test_model_risk(self): :cause=nature.earthquake :provider={[ ou:org=* :name="desert power" ]} :provider:name="desert power" - :attack={[ risk:attack=* ]} :reporter={ ou:org:name=vertex } :reporter:name=vertex + <(ledto)+ {[ risk:attack=* ]} ] ''') self.len(1, nodes) - self.nn(nodes[0].get('attack')) self.nn(nodes[0].get('reporter')) self.eq('the big one', nodes[0].get('name')) self.eq('vertex', nodes[0].get('reporter:name')) @@ -469,7 +461,7 @@ async def test_model_risk(self): self.eq('nature.earthquake.', nodes[0].get('cause')) self.eq((1672531200000000, 1704067200000000, 31536000000000), nodes[0].get('period')) - self.len(1, await core.nodes('risk:outage -> risk:attack')) + self.len(1, await core.nodes('risk:outage <(ledto)- risk:attack')) 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"')) diff --git a/synapse/tests/test_model_transport.py b/synapse/tests/test_model_transport.py index b7e56628aa..eb0aadb855 100644 --- a/synapse/tests/test_model_transport.py +++ b/synapse/tests/test_model_transport.py @@ -307,7 +307,7 @@ async def test_model_transport_rail(self): nodes = await core.nodes('''[ transport:occupant=* :role=passenger - :contact={[ entity:contact=({"name": "visi"}) ]} + :actor={[ entity:contact=({"name": "visi"}) ]} :trip={ transport:rail:train } :vehicle={ transport:rail:consist } :seat=2c @@ -317,7 +317,7 @@ async def test_model_transport_rail(self): :disembarked:point=2c :disembarked:place={ geo:place:name="union station" } ]''') - self.nn(nodes[0].get('contact')) + self.nn(nodes[0].get('actor')) self.eq('2c', nodes[0].get('seat')) self.eq('passenger.', nodes[0].get('role')) self.eq('transport:rail:train', nodes[0].get('trip')[0])