├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── gov │ │ └── ornl │ │ └── stucco │ │ ├── RelationExtractor.java │ │ ├── graph │ │ ├── Edge.java │ │ └── Vertex.java │ │ ├── model │ │ └── CyberRelation.java │ │ └── pattern │ │ ├── ExactPattern.java │ │ ├── FuzzyPattern.java │ │ ├── MatchingPattern.java │ │ ├── ParseTreePattern.java │ │ ├── Pattern.java │ │ ├── Patterns.java │ │ ├── elements │ │ ├── CyberEntity.java │ │ ├── POS.java │ │ ├── PatternElement.java │ │ ├── Token.java │ │ └── TreeElement.java │ │ └── utils │ │ └── PatternLoader.java └── resources │ ├── patterns │ └── patterns_relations.json │ └── patterns_relations_abbrev.json └── test └── java └── gov └── ornl └── stucco └── RelationExtractorTest.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.DS_Store 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | relation-extractor 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java/gov/ornl/stucco/RelationExtractor.java=UTF-8 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 4 | org.eclipse.jdt.core.compiler.compliance=1.7 5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 7 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 8 | org.eclipse.jdt.core.compiler.source=1.7 9 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk7 4 | - openjdk7 5 | - oraclejdk8 6 | before_install: 7 | - mvn scm:checkout -Dmodule.name=entity-extractor 8 | - cd entity-extractor 9 | - mvn clean install 10 | - cd .. 11 | after_success: 12 | - wget https://raw.githubusercontent.com/stucco/test/master/rerun-test.sh 13 | - chmod a+x ./rerun-test.sh 14 | - ./rerun-test.sh -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is freely distributable under the terms of the MIT License. 2 | 3 | Copyright (c) UT-Battelle, LLC (the "Original Author") 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS, THE U.S. GOVERNMENT, OR UT-BATTELLE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Relation Extraction 2 | Library to create vertices and edges from a document annotated with cyber-entity labels and a list of relationship patterns between these cyber entities. 3 | 4 | ## Dependency 5 | The Relation Extraction library shares object models with the Entity Extraction library. To install the dependency locally: 6 | 7 | mvn scm:checkout -Dmodule.name=entity-extractor 8 | cd entity-extractor 9 | mvn clean install 10 | 11 | ## Entity Types 12 | * Software 13 | * Vendor 14 | * Product 15 | * Version 16 | * File 17 | * Name 18 | * Function 19 | * Name 20 | * Vulnerability 21 | * Name 22 | * Description 23 | * CVE 24 | * MS 25 | 26 | ## Relationship Types 27 | * ExploitTargetRelatedObservable Edge 28 | 29 | Exploit Target (e.g. vulnerability) --> Observable (e.g. software) 30 | 31 | * Sub-Observable Edge 32 | 33 | Observable (e.g. software) --> Observable (e.g. file) 34 | 35 | * Software, File, Function, Vulnerability Vertices 36 | 37 | Software/file/function/vulnerability properties are part of the same vertex 38 | 39 | Example: "... **MS15-035**, which addresses a **remote code execution** bug ..." 40 | "MS15-035" is extracted as a vulnerability MS property, and "remote code execution" is extracted as a vulnerability description property. This type of relationship indicates that both properties are describing the same vulnerability object. 41 | 42 | 43 | ## Relationship Patterns 44 | These patterns are defined in a JSON-formatted file. There are three types of patterns we will need to find: 45 | 46 | * The exact-match pattern consists of at least two cyber-entity labels, and words or part-of-speech (pos) tags. The annotated document must match the pattern exactly with no extra words. 47 | 48 | Example Text: “Tomcat from Apache” 49 | Pattern: , "from", 50 | 51 | * The fuzzy-match pattern begins and ends with cyber-entity labels, but it allows for extra words to be present in the document that are not reflected in the pattern. The words, or pos tags, in the pattern must appear in the annotated document, but there can be extra words as well. The only restriction is the two entities are no more than 10 words apart in the document. 52 | 53 | Example Text: “Apache Tomcat’s latest update 8.0.22” 54 | Pattern: , “update”, 55 | 56 | * The parse-tree-path pattern defines a path through a sentence’s parse tree, where the first and last elements of the path are labeled cyber entities. 57 | 58 | Example Parse Tree: (NP (NP (NNP Apache) (NNP Tomcat) (POS 's)) (JJS latest)) 59 | Pattern: -- NNP -- NP -- NNP -- 60 | 61 | The majority of the knowledge graph's ontology is defined within this file, so the file can be modified while the extractor's mechanics remain the same. The file format is as follows: 62 | 63 | {"Patterns": [ 64 | {"edgeType": "ExploitTargetRelatedObservable", "patternType": "ExactPattern", "patternSequence": [{"class": "CyberEntity", "value": "sw.product", "vType": "inV"}, {"class": "Token", "value": "update"}, {"class": "Token", "value": "-LRB-"}, {"class": "CyberEntity", "value": "vuln.name", "vType": "outV"}]}, 65 | {"vertexType": "software", "patternType": "ExactPattern", "patternSequence": [{"class": "Token", "value": "versions"}, {"class": "Token", "value": "of"}, {"class": "CyberEntity", "value": "sw.product"}, {"class": "Token", "value": "are"}, {"class": "CyberEntity", "value": "sw.version"}, {"class": "POS", "value": "IN"}, {"class": "CyberEntity", "value": "sw.version"}]}, 66 | {"vertexType": "software", "patternType": "ParseTreePattern", "patternSequence": [{"class": "CyberEntity", "value": "sw.product"}, {"class": "TreeElement", "value": "NNP"}, ...]}, 67 | ... 68 | ] } 69 | 70 | 71 | ## Input 72 | * Output from the [Entity-Extractor](https://github.com/stucco/entity-extractor) as an Annotation object, which represents the sentences, list of words from the text, along with each word's part of speech tag and cyber domain label. 73 | * The String name of the document's source 74 | * A JSON file containing the set of patterns to find in the annotated document 75 | 76 | 77 | ## Current Process 78 | * For each pattern: 79 | * Search the document for candidate instances and for each instance: 80 | * If the pattern type is ExactPattern, then all tokens listed in the pattern sequence must match 81 | * If the pattern type is FuzzyPattern, then all the cyber entities must match those in the pattern sequence with no more than 10 words between them, but there can be other tokens in the document that are not represented in the pattern sequence 82 | * If the pattern type is ParseTreePattern, then the pattern sequence represents a path, between cyber entities, in the parse tree and all elements must match exactly 83 | 84 | 85 | ## Output 86 | * A JSON-formatted subgraph of the vertices and edges is created, which loosely resembles the STIX data model 87 | 88 | ``` 89 | { "vertices" : 90 | { 91 | id1 : { "vertexType": "software", "vendor": "Apache", "product": "Tomcat"}, 92 | id2 : { "vertexType": "vulnerability", "description": "buffer overflow"}, 93 | ... 94 | }, 95 | "edges" : 96 | [ 97 | {"id": "vuln_Tomcat_1234", "inV": "id1", "outV": "id2", "label": "ExploitTargetRelatedObservable"}, 98 | ... 99 | ] 100 | } 101 | ``` 102 | 103 | ## Usage 104 | EntityLabeler labeler = new EntityLabeler(); 105 | Annotation doc = labeler.getAnnotatedDoc("My Doc", exampleText); 106 | RelationExtractor rx = new RelationExtractor(); 107 | String graph = rx.createSubgraph(doc, "My source"); 108 | 109 | ## Test 110 | 1) Install the dependency as described [above](https://github.com/stucco/relation-extractor#dependency) 111 | 112 | 2) Run the commands: 113 | 114 | mvn clean package 115 | mvn test 116 | 117 | ## License 118 | This software is freely distributable under the terms of the MIT License. 119 | 120 | Copyright (c) UT-Battelle, LLC (the "Original Author") 121 | 122 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 123 | 124 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 125 | 126 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS, THE U.S. GOVERNMENT, OR UT-BATTELLE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 127 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | gov.ornl.stucco 4 | relation-extractor 5 | 1.0.0 6 | 7 | UTF-8 8 | true 9 | gov.ornl.stucco.RelationExtractor 10 | 11 | 12 | 13 | gov.ornl.stucco 14 | entity-extractor 15 | 1.0.0 16 | 17 | 18 | edu.stanford.nlp 19 | stanford-corenlp 20 | 3.4.1 21 | 22 | 23 | edu.stanford.nlp 24 | stanford-corenlp 25 | 3.4.1 26 | models 27 | 28 | 29 | com.fasterxml.jackson.core 30 | jackson-databind 31 | 2.7.0 32 | 33 | 34 | junit 35 | junit 36 | 4.8.1 37 | test 38 | 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-scm-plugin 45 | 1.9 46 | 47 | scm:git:https://github.com/stucco/${module.name}.git 48 | ${module.name} 49 | 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-compiler-plugin 54 | 3.1 55 | 56 | 1.7 57 | 1.7 58 | true 59 | true 60 | true 61 | 62 | 63 | 64 | compile 65 | 66 | compile 67 | 68 | 69 | 70 | 71 | 72 | org.codehaus.mojo 73 | exec-maven-plugin 74 | 1.2.1 75 | 76 | 77 | 78 | exec 79 | 80 | 81 | 82 | 83 | java 84 | true 85 | false 86 | compile 87 | ${main.class} 88 | 89 | 90 | 91 | 92 | 93 | src/main/resources 94 | 95 | patterns/patterns_relations.json 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/RelationExtractor.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco; 2 | 3 | import edu.stanford.nlp.ling.CoreAnnotations.SentencesAnnotation; 4 | import edu.stanford.nlp.ling.CoreAnnotations.TextAnnotation; 5 | import edu.stanford.nlp.ling.CoreAnnotations.TokensAnnotation; 6 | import edu.stanford.nlp.ling.CoreLabel; 7 | import edu.stanford.nlp.pipeline.Annotation; 8 | import edu.stanford.nlp.util.CoreMap; 9 | import gov.ornl.stucco.entity.CyberEntityAnnotator.CyberAnnotation; 10 | import gov.ornl.stucco.entity.EntityLabeler; 11 | import gov.ornl.stucco.graph.Edge; 12 | import gov.ornl.stucco.graph.Vertex; 13 | import gov.ornl.stucco.model.CyberRelation; 14 | import gov.ornl.stucco.pattern.MatchingPattern; 15 | import gov.ornl.stucco.pattern.Pattern; 16 | import gov.ornl.stucco.pattern.Patterns; 17 | import gov.ornl.stucco.pattern.utils.PatternLoader; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * Main class responsible for creating the vertices from labeled entities, 24 | * and edges from interpreted relationships between the entities. 25 | * 26 | */ 27 | public class RelationExtractor { 28 | 29 | public static final String SOURCE_PROPERTY = "source"; 30 | private static final String DEFAULT_PATTERNS = "patterns/patterns_relations.json"; 31 | 32 | private Patterns patterns; 33 | private List relationships; 34 | 35 | public RelationExtractor() { 36 | this(DEFAULT_PATTERNS); 37 | } 38 | 39 | public RelationExtractor(String patternFile) { 40 | this.patterns = PatternLoader.loadPatterns(patternFile); 41 | this.relationships = new ArrayList(); 42 | } 43 | 44 | private void findPatterns(Annotation doc, String source) { 45 | for (Pattern pattern : patterns.getPatterns()) { 46 | MatchingPattern patternClass = pattern.getPattern(); 47 | relationships.addAll(patternClass.findPattern(doc)); 48 | } 49 | } 50 | 51 | /** 52 | * @param doc annotated version of the unstructured text 53 | * @param source name of the data source 54 | * @return graph in GraphSON format 55 | */ 56 | public String createSubgraph(Annotation doc, String source) { 57 | this.findPatterns(doc, source); 58 | 59 | List vertices = new ArrayList(); 60 | List edges = new ArrayList(); 61 | StringBuilder graphBuilder = new StringBuilder(); 62 | for (CyberRelation relation : relationships) { 63 | List relationVertices = relation.getVertexList(source); 64 | List relationEdges = relation.getEdgeList(relationVertices, source); 65 | vertices.addAll(relationVertices); 66 | edges.addAll(relationEdges); 67 | } 68 | 69 | graphBuilder.append("{\"vertices\": {"); 70 | for (int i=0; i sentences = doc.get(SentencesAnnotation.class); 107 | for ( CoreMap sentence : sentences) { 108 | // Label cyber entities appropriately 109 | for ( CoreLabel token : sentence.get(TokensAnnotation.class)) { 110 | System.out.println(token.get(TextAnnotation.class) + "\t\t" + token.get(CyberAnnotation.class)); 111 | } 112 | System.out.println(); 113 | } 114 | 115 | RelationExtractor rx = new RelationExtractor("src/main/resources/patterns_relations_abbrev.json"); 116 | System.out.println(rx.createSubgraph(doc, "CNN")); 117 | 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/graph/Edge.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.graph; 2 | 3 | 4 | /** 5 | * Represents the relationship between two entities, where the entities are vertices 6 | * and the relationship is an edge in the knowledge graph. 7 | */ 8 | public class Edge { 9 | private static final String _type = "edge"; 10 | 11 | private String _id; 12 | private String relation; 13 | private String inVertID; 14 | private String outVertID; 15 | private String inVType; 16 | private String outVType; 17 | private String source; 18 | 19 | 20 | public Edge(String id, String label, String inV, String inVType, String outV, String outVType, String source) { 21 | this._id = id; 22 | this.relation = label; 23 | this.inVertID = inV; 24 | this.inVType = inVType; 25 | this.outVertID = outV; 26 | this.outVType = outVType; 27 | this.source = source; 28 | } 29 | 30 | public String get_id() { 31 | return _id; 32 | } 33 | 34 | public void set_id(String _id) { 35 | this._id = _id; 36 | } 37 | 38 | public String get_label() { 39 | return relation; 40 | } 41 | 42 | public void set_label(String _label) { 43 | this.relation = _label; 44 | } 45 | 46 | public String get_inV() { 47 | return inVertID; 48 | } 49 | 50 | public void set_inV(String _inV) { 51 | this.inVertID = _inV; 52 | } 53 | 54 | public String get_outV() { 55 | return outVertID; 56 | } 57 | 58 | public void set_outV(String _outV) { 59 | this.outVertID = _outV; 60 | } 61 | 62 | public String getInVType() { 63 | return inVType; 64 | } 65 | 66 | public void setInVType(String inVType) { 67 | this.inVType = inVType; 68 | } 69 | 70 | public String getOutVType() { 71 | return outVType; 72 | } 73 | 74 | public void setOutVType(String outVType) { 75 | this.outVType = outVType; 76 | } 77 | 78 | public String getSource() { 79 | return source; 80 | } 81 | 82 | public void setSource(String source) { 83 | this.source = source; 84 | } 85 | 86 | public static String getType() { 87 | return _type; 88 | } 89 | 90 | @Override 91 | public int hashCode() { 92 | final int prime = 31; 93 | int result = 1; 94 | result = prime * result + ((_id == null) ? 0 : _id.hashCode()); 95 | result = prime * result + ((inVertID == null) ? 0 : inVertID.hashCode()); 96 | result = prime * result + ((relation == null) ? 0 : relation.hashCode()); 97 | result = prime * result + ((outVertID == null) ? 0 : outVertID.hashCode()); 98 | result = prime * result + ((inVType == null) ? 0 : inVType.hashCode()); 99 | result = prime * result 100 | + ((outVType == null) ? 0 : outVType.hashCode()); 101 | result = prime * result + ((source == null) ? 0 : source.hashCode()); 102 | return result; 103 | } 104 | 105 | @Override 106 | public boolean equals(Object obj) { 107 | if (this == obj) 108 | return true; 109 | if (obj == null) 110 | return false; 111 | if (getClass() != obj.getClass()) 112 | return false; 113 | Edge other = (Edge) obj; 114 | if (_id == null) { 115 | if (other._id != null) 116 | return false; 117 | } else if (!_id.equals(other._id)) 118 | return false; 119 | if (inVertID == null) { 120 | if (other.inVertID != null) 121 | return false; 122 | } else if (!inVertID.equals(other.inVertID)) 123 | return false; 124 | if (relation == null) { 125 | if (other.relation != null) 126 | return false; 127 | } else if (!relation.equals(other.relation)) 128 | return false; 129 | if (outVertID == null) { 130 | if (other.outVertID != null) 131 | return false; 132 | } else if (!outVertID.equals(other.outVertID)) 133 | return false; 134 | if (inVType == null) { 135 | if (other.inVType != null) 136 | return false; 137 | } else if (!inVType.equals(other.inVType)) 138 | return false; 139 | if (outVType == null) { 140 | if (other.outVType != null) 141 | return false; 142 | } else if (!outVType.equals(other.outVType)) 143 | return false; 144 | if (source == null) { 145 | if (other.source != null) 146 | return false; 147 | } else if (!source.equals(other.source)) 148 | return false; 149 | return true; 150 | } 151 | 152 | @Override 153 | public String toString() { 154 | return "[_id=" + _id + ", relation=" + relation + ", inVertID=" + inVertID 155 | + ", outVertID=" + outVertID + ", inVType=" + inVType + ", outVType=" 156 | + outVType + ", source=" + source + "]"; 157 | } 158 | 159 | public String toGraphSON() { 160 | StringBuilder graph = new StringBuilder(); 161 | 162 | graph.append("{"); 163 | 164 | graph.append("\"inVertID\":\""); 165 | graph.append(inVertID); 166 | graph.append("\","); 167 | 168 | graph.append("\"outVertID\":\""); 169 | graph.append(outVertID); 170 | graph.append("\","); 171 | 172 | graph.append("\"relation\":\""); 173 | graph.append(relation); 174 | 175 | graph.append("\"}"); 176 | 177 | return graph.toString(); 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/graph/Vertex.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.graph; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Represents an entity in the knowledge graph. 8 | * 9 | */ 10 | public class Vertex { 11 | private static long uid = 1234; 12 | 13 | private String _id; 14 | private String name; 15 | private String vertexType; 16 | private Map properties; 17 | 18 | public Vertex(String id, String type) { 19 | this._id = id; 20 | this.name = id; 21 | this.vertexType = type; 22 | this.properties = new HashMap(); 23 | } 24 | 25 | public Vertex(String type) { 26 | this(String.valueOf(getNextUID()), type); 27 | } 28 | 29 | public String get_id() { 30 | return _id; 31 | } 32 | 33 | public void set_id(String _id) { 34 | this._id = _id; 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public String getVertexType() { 46 | return vertexType; 47 | } 48 | 49 | public void setVertexType(String vertexType) { 50 | this.vertexType = vertexType; 51 | } 52 | 53 | public Map getProperties() { 54 | return properties; 55 | } 56 | 57 | public void setProperties(Map properties) { 58 | this.properties = properties; 59 | } 60 | 61 | public void addProperty(String propertyName, String propertyValue) { 62 | properties.put(propertyName, propertyValue); 63 | } 64 | 65 | public String getProperty(String propertyName) { 66 | String value = null; 67 | if (properties.containsKey(propertyName)) { 68 | value = (String) properties.get(propertyName); 69 | } 70 | return value; 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | final int prime = 31; 76 | int result = 1; 77 | result = prime * result + ((_id == null) ? 0 : _id.hashCode()); 78 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 79 | result = prime * result 80 | + ((properties == null) ? 0 : properties.hashCode()); 81 | result = prime * result 82 | + ((vertexType == null) ? 0 : vertexType.hashCode()); 83 | return result; 84 | } 85 | 86 | @Override 87 | public boolean equals(Object obj) { 88 | if (this == obj) 89 | return true; 90 | if (obj == null) 91 | return false; 92 | if (getClass() != obj.getClass()) 93 | return false; 94 | Vertex other = (Vertex) obj; 95 | if (_id == null) { 96 | if (other._id != null) 97 | return false; 98 | } else if (!_id.equals(other._id)) 99 | return false; 100 | if (name == null) { 101 | if (other.name != null) 102 | return false; 103 | } else if (!name.equals(other.name)) 104 | return false; 105 | if (properties == null) { 106 | if (other.properties != null) 107 | return false; 108 | } else if (!properties.equals(other.properties)) 109 | return false; 110 | if (vertexType == null) { 111 | if (other.vertexType != null) 112 | return false; 113 | } else if (!vertexType.equals(other.vertexType)) 114 | return false; 115 | return true; 116 | } 117 | 118 | @Override 119 | public String toString() { 120 | return "[_id=" + _id + ", name=" + name + ", vertexType=" 121 | + vertexType + ", properties=" + properties + "]"; 122 | } 123 | 124 | public String toJSON() { 125 | StringBuilder graph = new StringBuilder(); 126 | graph.append("\""); 127 | graph.append(_id); 128 | graph.append("\": {"); 129 | 130 | graph.append("\"name\":\""); 131 | graph.append(name); 132 | graph.append("\","); 133 | 134 | graph.append("\"vertexType\":\""); 135 | if (vertexType.equalsIgnoreCase("sw")) { 136 | graph.append("software"); 137 | } 138 | else if (vertexType.equalsIgnoreCase("vuln")) { 139 | graph.append("vulnerability"); 140 | } 141 | else { 142 | graph.append(vertexType); 143 | } 144 | graph.append("\","); 145 | 146 | for(String prop : properties.keySet()) { 147 | graph.append("\""); 148 | graph.append(prop); 149 | graph.append("\":\""); 150 | graph.append(properties.get(prop)); 151 | graph.append("\","); 152 | } 153 | graph.deleteCharAt(graph.length()-1); 154 | 155 | graph.append("}"); 156 | 157 | return graph.toString(); 158 | } 159 | 160 | private static long getNextUID() { 161 | uid = uid + 1; 162 | return uid; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/model/CyberRelation.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.model; 2 | 3 | import gov.ornl.stucco.RelationExtractor; 4 | import gov.ornl.stucco.entity.models.CyberEntityMention; 5 | import gov.ornl.stucco.graph.Edge; 6 | import gov.ornl.stucco.graph.Vertex; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | public class CyberRelation { 14 | 15 | private List cyberEntities; 16 | private String relationshipName; 17 | private boolean isEdge; 18 | private Set inVTypes; 19 | private Set outVTypes; 20 | 21 | public CyberRelation(List entities, String relationship, boolean isEdge, Set inVTypes, Set outVTypes) { 22 | this.cyberEntities = entities; 23 | this.relationshipName = relationship; 24 | this.isEdge = isEdge; 25 | this.inVTypes = inVTypes; 26 | this.outVTypes = outVTypes; 27 | } 28 | 29 | public CyberRelation(List entities, String relationship, boolean isEdge) { 30 | this(entities, relationship, isEdge, new HashSet(), new HashSet()); 31 | } 32 | 33 | public CyberRelation(String relationship, boolean isEdge) { 34 | this(new ArrayList(), relationship, isEdge); 35 | } 36 | 37 | public List getCyberEntities() { 38 | return cyberEntities; 39 | } 40 | 41 | public void setCyberEntities(List cyberEntities) { 42 | this.cyberEntities = cyberEntities; 43 | } 44 | 45 | public void addCyberEntity(CyberEntityMention cyberEntity) { 46 | this.cyberEntities.add(cyberEntity); 47 | } 48 | 49 | public String getRelationshipName() { 50 | return relationshipName; 51 | } 52 | 53 | public void setRelationshipName(String relationshipName) { 54 | this.relationshipName = relationshipName; 55 | } 56 | 57 | public boolean isEdge() { 58 | return isEdge; 59 | } 60 | 61 | public void setIsEdge(boolean isEdge) { 62 | this.isEdge = isEdge; 63 | } 64 | 65 | public void addInVType(String inVType) { 66 | this.inVTypes.add(inVType); 67 | } 68 | 69 | public void addOutVType(String outVType) { 70 | this.outVTypes.add(outVType); 71 | } 72 | 73 | public void setInVType(Set inVTypeSet) { 74 | this.inVTypes = inVTypeSet; 75 | } 76 | 77 | public void setOutVType(Set outVTypeSet) { 78 | this.outVTypes = outVTypeSet; 79 | } 80 | 81 | public List getVertexList(String source) { 82 | List vertices = new ArrayList(); 83 | if (this.isEdge) { 84 | vertices = createVertexList(source); 85 | } 86 | else { 87 | Vertex v = createVertex(source); 88 | if (v != null) { 89 | vertices.add(v); 90 | } 91 | } 92 | return vertices; 93 | } 94 | 95 | private List createVertexList(String source) { 96 | List newVertices = new ArrayList(); 97 | if ((this.cyberEntities != null) && (!this.cyberEntities.isEmpty())) { 98 | 99 | for (CyberEntityMention mention : this.cyberEntities) { 100 | Vertex v = null; 101 | v = new Vertex(mention.getType()); 102 | 103 | boolean exists = false; 104 | for (int i=newVertices.size()-1; i>=0; i--) { 105 | Vertex existingV = newVertices.get(i); 106 | if ((existingV.getVertexType().equalsIgnoreCase(v.getVertexType())) && (existingV.getProperty(mention.getSubType()) == null)) { 107 | v = existingV; 108 | exists = true; 109 | break; 110 | } 111 | } 112 | v.addProperty(mention.getSubType(), mention.getValue()); 113 | v.addProperty(RelationExtractor.SOURCE_PROPERTY, source); 114 | if (!exists) { 115 | newVertices.add(v); 116 | } 117 | } 118 | 119 | } 120 | 121 | return newVertices; 122 | } 123 | 124 | private Vertex createVertex(String source) { 125 | Vertex v = null; 126 | 127 | if ((this.cyberEntities != null) && (!this.cyberEntities.isEmpty())) { 128 | v = new Vertex(this.relationshipName); 129 | 130 | for (CyberEntityMention mention : this.cyberEntities) { 131 | v.addProperty(mention.getSubType(), mention.getValue()); 132 | } 133 | v.addProperty(RelationExtractor.SOURCE_PROPERTY, source); 134 | 135 | } 136 | 137 | return v; 138 | } 139 | 140 | public List getEdgeList(List vertices, String source) { 141 | List edges = new ArrayList(); 142 | 143 | if (this.isEdge) { 144 | if ((vertices == null) || (vertices.isEmpty())) { 145 | vertices = getVertexList(source); 146 | } 147 | 148 | for (int i=0; i seq, String edgeType, String vertexType) { 26 | super(seq, edgeType, vertexType); 27 | } 28 | 29 | public List findPattern(Annotation doc) { 30 | List relationships = new ArrayList(); 31 | 32 | //get the cyber entity elements from the pattern to find in the sentence 33 | //Note: patterns have at least 2 cyber entities to make a vertex or edge 34 | int patternEntityIndex1 = this.getCyberEntityIndex(0); 35 | if (patternEntityIndex1 == -1) { 36 | return relationships; 37 | } 38 | CyberEntity patternEntity1 = (CyberEntity) sequence.get(patternEntityIndex1); 39 | int patternEntityIndex2 = this.getCyberEntityIndex(patternEntityIndex1+1); 40 | if (patternEntityIndex2 == -1) { 41 | return relationships; 42 | } 43 | CyberEntity patternEntity2 = (CyberEntity) sequence.get(patternEntityIndex2); 44 | int patternDistance = patternEntityIndex2 - patternEntityIndex1; 45 | 46 | //collect all document's cyber entity mentions 47 | //Note: patterns can span sentences 48 | //TODO: make CyberEntityMention a doc level annotation?? 49 | List entities = new ArrayList(); 50 | List sentences = doc.get(SentencesAnnotation.class); 51 | for (CoreMap sentence : sentences) { 52 | List sentenceEntities = sentence.get(CyberEntityMentionsAnnotation.class); 53 | if ((sentenceEntities != null) && (!sentenceEntities.isEmpty())) { 54 | entities.addAll(sentenceEntities); 55 | } 56 | } 57 | 58 | //there can be multiple instances of the same pattern in a given document 59 | for (int j=0; j (mention1.getSentence().get(TokensAnnotation.class)).size()) { 70 | startIndex = (mention1.getSentence().get(TokensAnnotation.class)).size() - startIndex; 71 | startIndex = startIndex * -1; 72 | } 73 | 74 | if ((mention2.getType().equalsIgnoreCase(patternEntity2.getType()) && mention2.getSubType().equalsIgnoreCase(patternEntity2.getSubType())) && (startIndex + patternDistance >= endIndex)) { 75 | if (startIndex + patternDistance == endIndex) { 76 | CyberRelation newRelationship = this.subDocumentMatch(doc, patternEntityIndex1, mention1, mention2, entities, i+1); 77 | if (newRelationship != null) { 78 | relationships.add(newRelationship); 79 | break; 80 | } 81 | else { 82 | //there will not be another possible mention2 within the right token distance 83 | break; 84 | } 85 | } 86 | } 87 | // there cannot be another mention2 that matches token distance 88 | else if (startIndex + patternDistance < endIndex) { 89 | break; 90 | } 91 | } 92 | 93 | } 94 | } 95 | 96 | return relationships; 97 | 98 | } 99 | 100 | 101 | private CyberRelation subDocumentMatch(Annotation doc, int patternIndex, CyberEntityMention mention1, CyberEntityMention mention2, List entities, int mentionIndex) { 102 | //collect the matching cyber entities to be used in the relationship 103 | List relationEntities = new ArrayList(); 104 | 105 | //gather the vertex type names that are the inVertex types and those that are the outVertex types 106 | // Set inVTypes = new HashSet(); 107 | // Set outVTypes = new HashSet(); 108 | 109 | CoreMap testSentence = mention1.getSentence(); 110 | int docIndex = mention1.getHeadTokenStart() - patternIndex; 111 | //if the sub document starts in the previous sentence from the first cyber entity, start there 112 | if (docIndex < 0) { 113 | testSentence = doc.get(SentencesAnnotation.class).get(mention1.getSentence().get(SentenceIndexAnnotation.class) - 1); 114 | docIndex = testSentence.get(TokensAnnotation.class).size() + (mention1.getHeadTokenStart() - patternIndex); 115 | } 116 | List tokens = testSentence.get(TokensAnnotation.class); 117 | CoreLabel candidateToken = tokens.get(docIndex); 118 | 119 | //match each pattern element to a consecutive element in the document 120 | for (int i=0; i= tokens.size()) { 126 | tokens = doc.get(SentencesAnnotation.class).get(testSentence.get(SentenceIndexAnnotation.class) + 1).get(TokensAnnotation.class); 127 | candidateToken = tokens.get(0); 128 | } 129 | else { 130 | candidateToken = tokens.get(mention1.getHeadTokenEnd()); 131 | } 132 | relationEntities.add(mention1); 133 | // if (this.edgeType != null) { 134 | // if (patternElement.getvType() == PatternElement.edgeVType.inV) { 135 | // inVTypes.add(mention1.getType()); 136 | // } 137 | // else if (patternElement.getvType() == PatternElement.edgeVType.outV) { 138 | // outVTypes.add(mention1.getType()); 139 | // } 140 | // else { 141 | // System.err.println("Warning: The ExactPattern with '" + this.sequence + "' has an invalid vType definition."); 142 | // } 143 | // } 144 | } 145 | else if (patternElement instanceof CyberEntity) { 146 | if ((mention2.getType().equalsIgnoreCase(((CyberEntity) patternElement).getType())) && (mention2.getSubType().equalsIgnoreCase(((CyberEntity) patternElement).getSubType()))) { 147 | //if the consecutive elements in the document overlap a sentence, go to the next sentence, and continue 148 | if ((mention2.getHeadTokenEnd()) >= tokens.size()) { 149 | tokens = doc.get(SentencesAnnotation.class).get(testSentence.get(SentenceIndexAnnotation.class) + 1).get(TokensAnnotation.class); 150 | candidateToken = tokens.get(0); 151 | } 152 | else { 153 | candidateToken = tokens.get(mention2.getHeadTokenEnd()); 154 | } 155 | relationEntities.add(mention2); 156 | // if (this.edgeType != null) { 157 | // if (patternElement.getvType() == PatternElement.edgeVType.inV) { 158 | // inVTypes.add(mention2.getType()); 159 | // } 160 | // else if (patternElement.getvType() == PatternElement.edgeVType.outV) { 161 | // outVTypes.add(mention2.getType()); 162 | // } 163 | // else { 164 | // System.err.println("Warning: The ExactPattern with '" + this.sequence + "' has an invalid vType definition."); 165 | // } 166 | // } 167 | } 168 | else if (!(candidateToken.get(CyberAnnotation.class).toString()).equalsIgnoreCase(patternElement.getValue())) { 169 | return null; 170 | } 171 | else { 172 | CyberEntityMention newMention = entities.get(mentionIndex); 173 | if(newMention.getHeadTokenStart() + 1 == candidateToken.index()) { 174 | if ((newMention.getType().equalsIgnoreCase(((CyberEntity) patternElement).getType())) && (newMention.getSubType().equalsIgnoreCase(((CyberEntity) patternElement).getSubType()))) { 175 | //if the consecutive elements in the document overlap a sentence, go to the next sentence, and continue 176 | if ((newMention.getHeadTokenEnd()) >= tokens.size()) { 177 | tokens = doc.get(SentencesAnnotation.class).get(testSentence.get(SentenceIndexAnnotation.class) + 1).get(TokensAnnotation.class); 178 | candidateToken = tokens.get(0); 179 | } 180 | else { 181 | candidateToken = tokens.get(newMention.getHeadTokenEnd()); 182 | } 183 | mentionIndex = mentionIndex + 1; 184 | relationEntities.add(newMention); 185 | // if (this.edgeType != null) { 186 | // if (patternElement.getvType() == PatternElement.edgeVType.inV) { 187 | // inVTypes.add(newMention.getType()); 188 | // } 189 | // else if (patternElement.getvType() == PatternElement.edgeVType.outV) { 190 | // outVTypes.add(newMention.getType()); 191 | // } 192 | // else { 193 | // System.err.println("Warning: The ExactPattern with '" + this.sequence + "' has an invalid vType definition."); 194 | // } 195 | // } 196 | } 197 | } 198 | } 199 | } 200 | else if (patternElement instanceof Token) { 201 | if (!candidateToken.get(TextAnnotation.class).equalsIgnoreCase(patternElement.getValue())) { 202 | return null; 203 | } 204 | //if the consecutive elements in the document overlap a sentence, go to the next sentence, and continue 205 | if ((candidateToken.index()) >= tokens.size()) { 206 | tokens = doc.get(SentencesAnnotation.class).get(testSentence.get(SentenceIndexAnnotation.class) + 1).get(TokensAnnotation.class); 207 | candidateToken = tokens.get(0); 208 | } 209 | else { 210 | candidateToken = tokens.get(candidateToken.index()); 211 | } 212 | } 213 | else if (patternElement instanceof POS) { 214 | if (!candidateToken.getString(PartOfSpeechAnnotation.class).equalsIgnoreCase(patternElement.getValue())) { 215 | return null; 216 | } 217 | //if the consecutive elements in the document overlap a sentence, go to the next sentence, and continue 218 | if ((candidateToken.index()) >= tokens.size()) { 219 | tokens = doc.get(SentencesAnnotation.class).get(testSentence.get(SentenceIndexAnnotation.class) + 1).get(TokensAnnotation.class); 220 | candidateToken = tokens.get(0); 221 | } 222 | else { 223 | candidateToken = tokens.get(candidateToken.index()); 224 | } 225 | } 226 | } 227 | 228 | //build the new cyber-domain vertex or multiple vertices and connecting edge 229 | CyberRelation newRelation = null; 230 | if (this.edgeType != null) { 231 | newRelation = new CyberRelation(relationEntities, this.edgeType, true, this.inVTypes, this.outVTypes); 232 | } 233 | else if (this.vertexType != null) { 234 | newRelation = new CyberRelation(relationEntities, this.vertexType, false); 235 | } 236 | 237 | return newRelation; 238 | } 239 | 240 | private int getCyberEntityIndex(int fromIndex) { 241 | if (fromIndex >= 0) { 242 | for (int i=fromIndex; i seq, String edgeType, String vertexType) { 29 | super(seq, edgeType, vertexType); 30 | } 31 | 32 | public List findPattern(Annotation doc) { 33 | List relationships = new ArrayList(); 34 | 35 | // Collect all document's cyber entity mentions 36 | // Note: patterns can span sentences 37 | //TODO: make CyberEntityMention a doc level annotation?? 38 | List entities = new ArrayList(); 39 | List sentences = doc.get(SentencesAnnotation.class); 40 | for (CoreMap sentence : sentences) { 41 | List sentenceEntities = sentence.get(CyberEntityMentionsAnnotation.class); 42 | if ((sentenceEntities != null) && (!sentenceEntities.isEmpty())) { 43 | entities.addAll(sentenceEntities); 44 | } 45 | } 46 | 47 | //gather the vertex type names that are the inVertex types and those that are the outVertex types 48 | // Set inVTypes = new HashSet(); 49 | // Set outVTypes = new HashSet(); 50 | 51 | // First and last elements of the sequence should be labeled cyber entities 52 | CyberEntity seqEntity1 = null; 53 | CyberEntity seqEntity2 = null; 54 | PatternElement seqElement = this.sequence.get(0); 55 | if (seqElement instanceof CyberEntity) { 56 | seqEntity1 = (CyberEntity)seqElement; 57 | } 58 | seqElement = this.sequence.get(this.sequence.size()-1); 59 | if (seqElement instanceof CyberEntity) { 60 | seqEntity2 = (CyberEntity)seqElement; 61 | } 62 | 63 | for (int i=0; i entityMentionList = new ArrayList(); 81 | entityMentionList.add(docEntity1); 82 | entityMentionList.add(docEntity2); 83 | if (this.edgeType != null) { 84 | CyberRelation newRelation = new CyberRelation(entityMentionList, this.edgeType, true, this.inVTypes, this.outVTypes); 85 | relationships.add(newRelation); 86 | } 87 | else if (this.vertexType != null) { 88 | CyberRelation newRelation = new CyberRelation(entityMentionList, this.vertexType, false); 89 | relationships.add(newRelation); 90 | } 91 | } 92 | } 93 | } 94 | else if (docSentence1.get(SentenceIndexAnnotation.class) + 1 == docSentence2.get(SentenceIndexAnnotation.class)) { 95 | int distance = (docSentence1.get(TokensAnnotation.class).size() - docEntity1.getHeadTokenEnd()) + docEntity2.getHeadTokenStart(); 96 | if (distance <= FUZZY_MATCH_DISTANCE) { 97 | if (isBetweenMatch(docSentence1, docSentence2, docEntity1, docEntity2)) { 98 | List entityMentionList = new ArrayList(); 99 | entityMentionList.add(docEntity1); 100 | entityMentionList.add(docEntity2); 101 | if (this.edgeType != null) { 102 | CyberRelation newRelation = new CyberRelation(entityMentionList, this.edgeType, true, this.inVTypes, this.outVTypes); 103 | relationships.add(newRelation); 104 | } 105 | else if (this.vertexType != null) { 106 | CyberRelation newRelation = new CyberRelation(entityMentionList, this.vertexType, false); 107 | relationships.add(newRelation); 108 | } 109 | } 110 | } 111 | } 112 | 113 | } 114 | 115 | } 116 | else { 117 | break; 118 | } 119 | } 120 | } 121 | 122 | return relationships; 123 | } 124 | 125 | 126 | private boolean isBetweenMatch(CoreMap docSentence1, CoreMap docSentence2, CyberEntityMention docEntity1, CyberEntityMention docEntity2) { 127 | List tokens = docSentence1.get(TokensAnnotation.class); 128 | int startTokenIndex = docEntity1.getHeadTokenEnd(); 129 | int endTokenIndex = docEntity2.getHeadTokenStart(); 130 | 131 | //Working with two consecutive sentence, or a single sentence 132 | if (docSentence2 != null) { 133 | endTokenIndex = tokens.size() + docEntity2.getHeadTokenStart() - 1; 134 | tokens.addAll(docSentence2.get(TokensAnnotation.class)); 135 | } 136 | 137 | // Loop through all tokens and see that each PatternElement appears 138 | int tokenIndex = startTokenIndex; 139 | int i; 140 | for (i=1; i endTokenIndex) { 159 | return false; 160 | } 161 | } 162 | // If we have matched all PatternElements, this subDocument is a match 163 | if (i == sequence.size()-1) { 164 | return true; 165 | } 166 | 167 | return false; 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/MatchingPattern.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern; 2 | 3 | import edu.stanford.nlp.pipeline.Annotation; 4 | import gov.ornl.stucco.model.CyberRelation; 5 | import gov.ornl.stucco.pattern.elements.CyberEntity; 6 | import gov.ornl.stucco.pattern.elements.PatternElement; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | public class MatchingPattern { 14 | 15 | protected List sequence; 16 | protected String edgeType; 17 | protected String vertexType; 18 | protected Set inVTypes; 19 | protected Set outVTypes; 20 | 21 | public MatchingPattern(List seq, String edgeType, String vertexType) { 22 | this.sequence = seq; 23 | this.edgeType = edgeType; 24 | this.vertexType = vertexType; 25 | inVTypes = new HashSet(); 26 | outVTypes = new HashSet(); 27 | collectVTypes(); 28 | } 29 | 30 | public List getSequence() { 31 | return sequence; 32 | } 33 | 34 | public void setSequence(List sequence) { 35 | this.sequence = sequence; 36 | } 37 | 38 | public String getEdgeType() { 39 | return edgeType; 40 | } 41 | 42 | public void setEdgeType(String edgeType) { 43 | this.edgeType = edgeType; 44 | } 45 | 46 | public String getVertexType() { 47 | return vertexType; 48 | } 49 | 50 | public void setVertexType(String vertexType) { 51 | this.vertexType = vertexType; 52 | } 53 | 54 | public Set getInVTypes() { 55 | return inVTypes; 56 | } 57 | 58 | public void setInVTypes(Set inVTypes) { 59 | this.inVTypes = inVTypes; 60 | } 61 | 62 | public void addInVType(String inVType) { 63 | this.inVTypes.add(inVType); 64 | } 65 | 66 | public Set getOutVTypes() { 67 | return outVTypes; 68 | } 69 | 70 | public void setOutVTypes(Set outVTypes) { 71 | this.outVTypes = outVTypes; 72 | } 73 | 74 | public void addOutVType(String outVType) { 75 | this.outVTypes.add(outVType); 76 | } 77 | 78 | public List findPattern(Annotation doc) { 79 | List relationships = new ArrayList(); 80 | return relationships; 81 | } 82 | 83 | private void collectVTypes() { 84 | if (this.edgeType != null) { 85 | for (int i=0; i seq, String edgeType, String vertexType) { 23 | super(seq, edgeType, vertexType); 24 | } 25 | 26 | // For each cyber entity mention in the sentence 27 | // 1) Find the head node of the subtree containing all words of the cyber entity mention 28 | // 2) Attempt to find this pattern's sequence of parse-tree elements between each pair of cyber entities 29 | // in this sentence. 30 | public List findPattern(Annotation doc) { 31 | List relationships = new ArrayList(); 32 | 33 | List sentences = doc.get(SentencesAnnotation.class); 34 | // Parse trees are only constructed per sentence, so there is no paths 35 | // that cross the sentence boundary. 36 | for (CoreMap sentence : sentences) { 37 | Tree rootTree = sentence.get(TreeAnnotation.class); 38 | List cyberMentions = sentence.get(CyberEntityMentionsAnnotation.class); 39 | if ((cyberMentions == null) || (cyberMentions.isEmpty())) { 40 | continue; 41 | } 42 | Map entitySubtreeMap = new HashMap(); 43 | 44 | // Gather subtrees of cyber entity mentions 45 | for (CyberEntityMention cyberMention : cyberMentions) { 46 | Tree sharedHead = getSharedHeadNode(cyberMention, rootTree); 47 | if (sharedHead != null) { 48 | entitySubtreeMap.put(cyberMention, sharedHead); 49 | } 50 | } 51 | 52 | // Attempt to find the pattern's sequence between every two cyber entity mentions 53 | Iterator cyberMentionIter = entitySubtreeMap.keySet().iterator(); 54 | while (cyberMentionIter.hasNext()) { 55 | CyberEntityMention cyberMention = cyberMentionIter.next(); 56 | Tree subTree = entitySubtreeMap.get(cyberMention); 57 | 58 | // Initial checks include the first cyber mention and its subtree root matches the first two element in the sequence, 59 | // and the second cyber mention, along with its subtree root matches the last two sequence element. Otherwise, 60 | // don't bother comparing the path between. 61 | if ((this.sequence.get(0) instanceof CyberEntity) && (this.sequence.get(this.sequence.size()-1) instanceof CyberEntity)) { 62 | CyberEntity seqFirst = (CyberEntity) this.sequence.get(0); 63 | CyberEntity seqLast = ((CyberEntity) this.sequence.get(this.sequence.size()-1)); 64 | 65 | if ((cyberMention.getType().equalsIgnoreCase(seqFirst.getType())) && (cyberMention.getSubType().equalsIgnoreCase(seqFirst.getSubType())) && ((subTree.label().toString()).equalsIgnoreCase(this.sequence.get(1).getValue()))) { 66 | for (CyberEntityMention cyberMention2 : entitySubtreeMap.keySet()) { 67 | Tree subTree2 = entitySubtreeMap.get(cyberMention2); 68 | if ((!cyberMention2.equals(cyberMention, true)) && (cyberMention2.getType().equalsIgnoreCase(seqLast.getType())) && (cyberMention2.getSubType().equalsIgnoreCase(seqLast.getSubType())) && ((subTree2.label().toString()).equalsIgnoreCase(this.sequence.get(this.sequence.size()-2).getValue()))) { 69 | 70 | // Compare the path between elements to the parse-tree portion of the sequence 71 | List pathBetween = rootTree.pathNodeToNode(subTree, subTree2); 72 | if (pathBetween != null) { 73 | int i; 74 | for (i=1; i relationEntityList = new ArrayList(); 84 | relationEntityList.add(cyberMention); 85 | relationEntityList.add(cyberMention2); 86 | if (this.edgeType != null) { 87 | CyberRelation newRelation = new CyberRelation(relationEntityList, this.edgeType, true, this.inVTypes, this.outVTypes); 88 | relationships.add(newRelation); 89 | } 90 | else if (this.vertexType != null) { 91 | CyberRelation newRelation = new CyberRelation(relationEntityList, this.vertexType, false); 92 | relationships.add(newRelation); 93 | } 94 | } 95 | } 96 | 97 | } 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | return relationships; 105 | } 106 | 107 | private Tree getSharedHeadNode(CyberEntityMention cyberMention, Tree rootTree) { 108 | Tree currentHead = null; 109 | 110 | // Match the cyber entity word(s) to the parse-tree leaf/leaves 111 | List leaves = rootTree.getLeaves(); 112 | int mentionStart = cyberMention.getHeadTokenStart(); 113 | int mentionEnd = cyberMention.getHeadTokenEnd() - 1; 114 | 115 | // Find the most dominate node in all paths from the first word in the cyber entity mention 116 | // to each of the other words. If this is a one-word cyber entity, then the POS of the word 117 | // will be the shared head node. 118 | Tree t1 = leaves.get(mentionStart); 119 | currentHead = t1.parent(rootTree); 120 | for (int i=mentionStart+1; i<=mentionEnd; i++) { 121 | Tree t2 = leaves.get(i); 122 | List path = rootTree.pathNodeToNode(t1.parent(rootTree), t2.parent(rootTree)); 123 | if (path != null) { 124 | for (Tree pathTree : path) { 125 | if (pathTree.dominates(currentHead)) { 126 | currentHead = pathTree; 127 | } 128 | } 129 | } 130 | } 131 | 132 | return currentHead; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/Pattern.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern; 2 | 3 | import gov.ornl.stucco.pattern.elements.PatternElement; 4 | 5 | import java.io.Serializable; 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.util.List; 9 | 10 | import com.fasterxml.jackson.annotation.JsonGetter; 11 | import com.fasterxml.jackson.annotation.JsonIgnore; 12 | import com.fasterxml.jackson.annotation.JsonProperty; 13 | import com.fasterxml.jackson.annotation.JsonSetter; 14 | 15 | public class Pattern implements Serializable { 16 | private static final long serialVersionUID = 1L; 17 | 18 | @JsonProperty("edgeType") 19 | private String edgeType; 20 | 21 | @JsonProperty("vertexType") 22 | private String vertexType; 23 | 24 | @JsonProperty("patternType") 25 | private String patternType; 26 | 27 | @JsonProperty("patternSequence") 28 | private List patternSequence; 29 | 30 | @JsonIgnore 31 | private MatchingPattern pattern; 32 | 33 | @JsonGetter("edgeType") 34 | public String getEdgeType() { 35 | return edgeType; 36 | } 37 | 38 | @JsonSetter("edgeType") 39 | public void setEdgeType(String edgeType) { 40 | this.edgeType = edgeType; 41 | } 42 | 43 | @JsonGetter("vertexType") 44 | public String getVertexType() { 45 | return vertexType; 46 | } 47 | 48 | @JsonSetter("vertexType") 49 | public void setVertexType(String vertexType) { 50 | this.vertexType = vertexType; 51 | } 52 | 53 | @JsonGetter("patternType") 54 | public String getPatternType() { 55 | return patternType; 56 | } 57 | 58 | @JsonSetter("patternType") 59 | public void setPatternType(String patternType) { 60 | this.patternType = patternType; 61 | } 62 | 63 | @JsonSetter("patternSequence") 64 | public void setPatternSequence(List patternSequence) { 65 | this.patternSequence = patternSequence; 66 | } 67 | 68 | @JsonGetter("patternSequence") 69 | public List getPatternSequence() { 70 | return patternSequence; 71 | } 72 | 73 | public MatchingPattern getPattern() { 74 | this.createPattern(); 75 | return pattern; 76 | } 77 | 78 | public void createPattern() { 79 | String patternClassName = Pattern.class.getPackage().getName() + "." + this.patternType; 80 | try { 81 | Class clas = Pattern.class.getClassLoader().loadClass(patternClassName); 82 | Constructor constructor = clas.getConstructor(List.class, String.class, String.class); 83 | this.pattern = (MatchingPattern) constructor.newInstance(this.patternSequence, this.edgeType, this.vertexType); 84 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | 89 | @Override 90 | public String toString() { 91 | return "Pattern [edgeType=" + edgeType + ", vertexType=" + vertexType 92 | + ", patternType=" + patternType + ", patternSequence=" 93 | + patternSequence + "]"; 94 | } 95 | 96 | @Override 97 | public int hashCode() { 98 | final int prime = 31; 99 | int result = 1; 100 | result = prime * result 101 | + ((edgeType == null) ? 0 : edgeType.hashCode()); 102 | result = prime * result 103 | + ((patternSequence == null) ? 0 : patternSequence.hashCode()); 104 | result = prime * result 105 | + ((patternType == null) ? 0 : patternType.hashCode()); 106 | result = prime * result 107 | + ((vertexType == null) ? 0 : vertexType.hashCode()); 108 | return result; 109 | } 110 | 111 | @Override 112 | public boolean equals(Object obj) { 113 | if (this == obj) 114 | return true; 115 | if (obj == null) 116 | return false; 117 | if (getClass() != obj.getClass()) 118 | return false; 119 | Pattern other = (Pattern) obj; 120 | if (edgeType == null) { 121 | if (other.edgeType != null) 122 | return false; 123 | } else if (!edgeType.equals(other.edgeType)) 124 | return false; 125 | if (patternSequence == null) { 126 | if (other.patternSequence != null) 127 | return false; 128 | } else if (!patternSequence.equals(other.patternSequence)) 129 | return false; 130 | if (patternType == null) { 131 | if (other.patternType != null) 132 | return false; 133 | } else if (!patternType.equals(other.patternType)) 134 | return false; 135 | if (vertexType == null) { 136 | if (other.vertexType != null) 137 | return false; 138 | } else if (!vertexType.equals(other.vertexType)) 139 | return false; 140 | return true; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/Patterns.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | import com.fasterxml.jackson.annotation.JsonGetter; 7 | import com.fasterxml.jackson.annotation.JsonProperty; 8 | import com.fasterxml.jackson.annotation.JsonSetter; 9 | 10 | public class Patterns implements Serializable { 11 | private static final long serialVersionUID = 1L; 12 | 13 | @JsonProperty("Patterns") 14 | private List patterns; 15 | 16 | @JsonGetter("Patterns") 17 | public List getPatterns() { 18 | return patterns; 19 | } 20 | 21 | @JsonSetter("Patterns") 22 | public void setPatterns(List patterns) { 23 | this.patterns = patterns; 24 | } 25 | 26 | public void addPattern(Pattern newPattern) { 27 | this.patterns.add(newPattern); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "Patterns [patterns=" + patterns + "]"; 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | final int prime = 31; 38 | int result = 1; 39 | result = prime * result 40 | + ((patterns == null) ? 0 : patterns.hashCode()); 41 | return result; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object obj) { 46 | if (this == obj) 47 | return true; 48 | if (obj == null) 49 | return false; 50 | if (getClass() != obj.getClass()) 51 | return false; 52 | Patterns other = (Patterns) obj; 53 | if (patterns == null) { 54 | if (other.patterns != null) 55 | return false; 56 | } else if (!patterns.equals(other.patterns)) 57 | return false; 58 | return true; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/elements/CyberEntity.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern.elements; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonSetter; 5 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 6 | import com.fasterxml.jackson.annotation.JsonTypeInfo.As; 7 | 8 | @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="class") 9 | public class CyberEntity extends PatternElement { 10 | 11 | @JsonIgnore 12 | private String type; 13 | 14 | @JsonIgnore 15 | private String subType; 16 | 17 | @Override 18 | @JsonSetter("value") 19 | public void setValue(String value) { 20 | this.value = value; 21 | 22 | //separate label 23 | if (super.getValue().contains(".")) { 24 | int index = super.getValue().indexOf("."); 25 | this.type = super.getValue().substring(0, index); 26 | this.subType = super.getValue().substring(index + 1); 27 | } 28 | else { 29 | this.type = super.getValue(); 30 | this.subType = ""; 31 | } 32 | } 33 | 34 | @JsonIgnore 35 | public String getType() { 36 | return type; 37 | } 38 | 39 | @JsonIgnore 40 | public String getSubType() { 41 | return subType; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "CyberEntity=" + value; 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/elements/POS.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern.elements; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo.As; 5 | 6 | @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="class") 7 | public class POS extends PatternElement { 8 | 9 | @Override 10 | public String toString() { 11 | return "POS=" + value; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/elements/PatternElement.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern.elements; 2 | 3 | import com.fasterxml.jackson.annotation.JsonGetter; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonSetter; 6 | import com.fasterxml.jackson.annotation.JsonSubTypes; 7 | import com.fasterxml.jackson.annotation.JsonSubTypes.Type; 8 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 9 | import com.fasterxml.jackson.annotation.JsonTypeInfo.As; 10 | 11 | @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="class") 12 | @JsonSubTypes({ 13 | @Type(value = CyberEntity.class), 14 | @Type(value = POS.class), 15 | @Type(value = Token.class), 16 | @Type(value = TreeElement.class) 17 | }) 18 | public abstract class PatternElement { 19 | 20 | public static enum edgeVType { 21 | outV, 22 | inV 23 | } 24 | 25 | @JsonProperty("value") 26 | protected String value; 27 | @JsonProperty("vType") 28 | protected edgeVType vType; 29 | 30 | @JsonGetter("value") 31 | public String getValue() { 32 | return value; 33 | } 34 | 35 | @JsonSetter("value") 36 | public void setValue(String value) { 37 | this.value = value; 38 | } 39 | 40 | @JsonGetter("vType") 41 | public edgeVType getvType() { 42 | return vType; 43 | } 44 | 45 | @JsonSetter("vType") 46 | public void setvType(edgeVType vType) { 47 | this.vType = vType; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "PatternElement [value=" + value + "]"; 53 | } 54 | 55 | @Override 56 | public int hashCode() { 57 | final int prime = 31; 58 | int result = 1; 59 | result = prime * result + ((value == null) ? 0 : value.hashCode()); 60 | return result; 61 | } 62 | 63 | @Override 64 | public boolean equals(Object obj) { 65 | if (this == obj) 66 | return true; 67 | if (obj == null) 68 | return false; 69 | if (getClass() != obj.getClass()) 70 | return false; 71 | PatternElement other = (PatternElement) obj; 72 | if (value == null) { 73 | if (other.value != null) 74 | return false; 75 | } else if (!value.equals(other.value)) 76 | return false; 77 | return true; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/elements/Token.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern.elements; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo.As; 5 | 6 | @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="class") 7 | public class Token extends PatternElement { 8 | 9 | @Override 10 | public String toString() { 11 | return "Token=" + value; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/elements/TreeElement.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern.elements; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo.As; 5 | 6 | @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="class") 7 | public class TreeElement extends PatternElement { 8 | 9 | @Override 10 | public String toString() { 11 | return "TreeElement=" + value; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/gov/ornl/stucco/pattern/utils/PatternLoader.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco.pattern.utils; 2 | 3 | import gov.ornl.stucco.pattern.Patterns; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.InputStream; 8 | 9 | import com.fasterxml.jackson.databind.DeserializationFeature; 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | 12 | public class PatternLoader { 13 | private static ObjectMapper mapper = new ObjectMapper(); 14 | 15 | public static Patterns loadPatterns(String patternFile) { 16 | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 17 | 18 | System.err.println("Loading relationship patterns from '" + patternFile + "'..."); 19 | Patterns patterns = null; 20 | try { 21 | InputStream inputStream = PatternLoader.class.getClassLoader().getResourceAsStream(patternFile); 22 | patterns = mapper.readValue(inputStream, Patterns.class); 23 | } catch (Exception e) { 24 | try { 25 | InputStream inputStream = new FileInputStream(new File(patternFile)); 26 | patterns = mapper.readValue(inputStream, Patterns.class); 27 | } catch (Exception ex) { 28 | ex.printStackTrace(); 29 | } 30 | } 31 | 32 | return patterns; 33 | } 34 | 35 | public static void main(String[] args) { 36 | Patterns patterns = PatternLoader.loadPatterns("patterns_relations.json"); 37 | System.out.println(patterns.toString()); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/resources/patterns/patterns_relations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Patterns": [ 3 | { 4 | "edgeType": "ExploitTargetRelatedObservable", 5 | "patternSequence": [ 6 | { 7 | "class": "CyberEntity", 8 | "vType": "inV", 9 | "value": "sw.product" 10 | }, 11 | { 12 | "class": "Token", 13 | "value": "update" 14 | }, 15 | { 16 | "class": "Token", 17 | "value": "-LRB-" 18 | }, 19 | { 20 | "class": "CyberEntity", 21 | "vType": "outV", 22 | "value": "vuln.name" 23 | } 24 | ], 25 | "patternType": "ExactPattern" 26 | }, 27 | { 28 | "patternSequence": [ 29 | { 30 | "class": "CyberEntity", 31 | "value": "vuln.description" 32 | }, 33 | { 34 | "class": "Token", 35 | "value": "-LRB-" 36 | }, 37 | { 38 | "class": "CyberEntity", 39 | "value": "vuln.name" 40 | } 41 | ], 42 | "patternType": "ExactPattern", 43 | "vertexType": "vulnerability" 44 | }, 45 | { 46 | "edgeType": "ExploitTargetRelatedObservable", 47 | "patternSequence": [ 48 | { 49 | "class": "CyberEntity", 50 | "vType": "outV", 51 | "value": "vuln.ms" 52 | }, 53 | { 54 | "class": "Token", 55 | "value": "," 56 | }, 57 | { 58 | "class": "Token", 59 | "value": "a" 60 | }, 61 | { 62 | "class": "Token", 63 | "value": "flaw" 64 | }, 65 | { 66 | "class": "Token", 67 | "value": "in" 68 | }, 69 | { 70 | "class": "Token", 71 | "value": "the" 72 | }, 73 | { 74 | "class": "CyberEntity", 75 | "vType": "inV", 76 | "value": "sw.product" 77 | } 78 | ], 79 | "patternType": "ExactPattern" 80 | }, 81 | { 82 | "edgeType": "ExploitTargetRelatedObservable", 83 | "patternSequence": [ 84 | { 85 | "class": "CyberEntity", 86 | "vType": "outV", 87 | "value": "vuln.ms" 88 | }, 89 | { 90 | "class": "Token", 91 | "value": "deals" 92 | }, 93 | { 94 | "class": "Token", 95 | "value": "with" 96 | }, 97 | { 98 | "class": "CyberEntity", 99 | "vType": "inV", 100 | "value": "sw.product" 101 | } 102 | ], 103 | "patternType": "ExactPattern" 104 | }, 105 | { 106 | "edgeType": "ExploitTargetRelatedObservable", 107 | "patternSequence": [ 108 | { 109 | "class": "CyberEntity", 110 | "vType": "inV", 111 | "value": "sw.product" 112 | }, 113 | { 114 | "class": "Token", 115 | "value": "update" 116 | }, 117 | { 118 | "class": "CyberEntity", 119 | "vType": "outV", 120 | "value": "vuln.ms" 121 | } 122 | ], 123 | "patternType": "ExactPattern" 124 | }, 125 | { 126 | "edgeType": "ExploitTargetRelatedObservable", 127 | "patternSequence": [ 128 | { 129 | "class": "CyberEntity", 130 | "vType": "outV", 131 | "value": "vuln.ms" 132 | }, 133 | { 134 | "class": "Token", 135 | "value": "," 136 | }, 137 | { 138 | "class": "Token", 139 | "value": "which" 140 | }, 141 | { 142 | "class": "Token", 143 | "value": "updates" 144 | }, 145 | { 146 | "class": "CyberEntity", 147 | "vType": "inV", 148 | "value": "sw.vendor" 149 | }, 150 | { 151 | "class": "Token", 152 | "value": "'s" 153 | }, 154 | { 155 | "class": "CyberEntity", 156 | "vType": "inV", 157 | "value": "sw.product" 158 | } 159 | ], 160 | "patternType": "ExactPattern" 161 | }, 162 | { 163 | "edgeType": "ExploitTargetRelatedObservable", 164 | "patternSequence": [ 165 | { 166 | "class": "CyberEntity", 167 | "vType": "inV", 168 | "value": "sw.product" 169 | }, 170 | { 171 | "class": "Token", 172 | "value": "-LRB-" 173 | }, 174 | { 175 | "class": "CyberEntity", 176 | "vType": "outV", 177 | "value": "vuln.ms" 178 | } 179 | ], 180 | "patternType": "ExactPattern" 181 | }, 182 | { 183 | "edgeType": "ExploitTargetRelatedObservable", 184 | "patternSequence": [ 185 | { 186 | "class": "CyberEntity", 187 | "vType": "outV", 188 | "value": "vuln.ms" 189 | }, 190 | { 191 | "class": "Token", 192 | "value": "flaw" 193 | }, 194 | { 195 | "class": "Token", 196 | "value": "in" 197 | }, 198 | { 199 | "class": "CyberEntity", 200 | "vType": "inV", 201 | "value": "sw.product" 202 | } 203 | ], 204 | "patternType": "FuzzyPattern" 205 | }, 206 | { 207 | "patternSequence": [ 208 | { 209 | "class": "CyberEntity", 210 | "value": "vuln.ms" 211 | }, 212 | { 213 | "class": "Token", 214 | "value": "is" 215 | }, 216 | { 217 | "class": "Token", 218 | "value": "also" 219 | }, 220 | { 221 | "class": "Token", 222 | "value": "related" 223 | }, 224 | { 225 | "class": "Token", 226 | "value": "to" 227 | }, 228 | { 229 | "class": "CyberEntity", 230 | "value": "vuln.description" 231 | } 232 | ], 233 | "patternType": "ExactPattern", 234 | "vertexType": "vulnerability" 235 | }, 236 | { 237 | "patternSequence": [ 238 | { 239 | "class": "CyberEntity", 240 | "value": "vuln.ms" 241 | }, 242 | { 243 | "class": "Token", 244 | "value": "which" 245 | }, 246 | { 247 | "class": "Token", 248 | "value": "addresses" 249 | }, 250 | { 251 | "class": "CyberEntity", 252 | "value": "vuln.description" 253 | } 254 | ], 255 | "patternType": "FuzzyPattern", 256 | "vertexType": "vulnerability" 257 | }, 258 | { 259 | "patternSequence": [ 260 | { 261 | "class": "CyberEntity", 262 | "value": "vuln.ms" 263 | }, 264 | { 265 | "class": "Token", 266 | "value": "which" 267 | }, 268 | { 269 | "class": "Token", 270 | "value": "addresses" 271 | }, 272 | { 273 | "class": "Token", 274 | "value": "another" 275 | }, 276 | { 277 | "class": "CyberEntity", 278 | "value": "vuln.description" 279 | } 280 | ], 281 | "patternType": "ExactPattern", 282 | "vertexType": "vulnerability" 283 | }, 284 | { 285 | "patternSequence": [ 286 | { 287 | "class": "CyberEntity", 288 | "value": "vuln.ms" 289 | }, 290 | { 291 | "class": "Token", 292 | "value": "is" 293 | }, 294 | { 295 | "class": "Token", 296 | "value": "also" 297 | }, 298 | { 299 | "class": "Token", 300 | "value": "a" 301 | }, 302 | { 303 | "class": "CyberEntity", 304 | "value": "vuln.description" 305 | } 306 | ], 307 | "patternType": "ExactPattern", 308 | "vertexType": "vulnerability" 309 | }, 310 | { 311 | "patternSequence": [ 312 | { 313 | "class": "CyberEntity", 314 | "value": "vuln.ms" 315 | }, 316 | { 317 | "class": "Token", 318 | "value": "," 319 | }, 320 | { 321 | "class": "Token", 322 | "value": "deals" 323 | }, 324 | { 325 | "class": "Token", 326 | "value": "with" 327 | }, 328 | { 329 | "class": "Token", 330 | "value": "a" 331 | }, 332 | { 333 | "class": "CyberEntity", 334 | "value": "vuln.description" 335 | } 336 | ], 337 | "patternType": "ExactPattern", 338 | "vertexType": "vulnerability" 339 | }, 340 | { 341 | "patternSequence": [ 342 | { 343 | "class": "CyberEntity", 344 | "value": "vuln.ms" 345 | }, 346 | { 347 | "class": "Token", 348 | "value": "relates" 349 | }, 350 | { 351 | "class": "Token", 352 | "value": "to" 353 | }, 354 | { 355 | "class": "Token", 356 | "value": "a" 357 | }, 358 | { 359 | "class": "CyberEntity", 360 | "value": "vuln.description" 361 | } 362 | ], 363 | "patternType": "ExactPattern", 364 | "vertexType": "vulnerability" 365 | }, 366 | { 367 | "patternSequence": [ 368 | { 369 | "class": "CyberEntity", 370 | "value": "vuln.ms" 371 | }, 372 | { 373 | "class": "Token", 374 | "value": "and" 375 | }, 376 | { 377 | "class": "Token", 378 | "value": "it" 379 | }, 380 | { 381 | "class": "Token", 382 | "value": "is" 383 | }, 384 | { 385 | "class": "Token", 386 | "value": "yet" 387 | }, 388 | { 389 | "class": "Token", 390 | "value": "another" 391 | }, 392 | { 393 | "class": "CyberEntity", 394 | "value": "vuln.description" 395 | } 396 | ], 397 | "patternType": "ExactPattern", 398 | "vertexType": "vulnerability" 399 | }, 400 | { 401 | "patternSequence": [ 402 | { 403 | "class": "CyberEntity", 404 | "value": "sw.product" 405 | }, 406 | { 407 | "class": "CyberEntity", 408 | "value": "sw.version" 409 | } 410 | ], 411 | "patternType": "ExactPattern", 412 | "vertexType": "software" 413 | }, 414 | { 415 | "patternSequence": [ 416 | { 417 | "class": "Token", 418 | "value": "versions" 419 | }, 420 | { 421 | "class": "Token", 422 | "value": "of" 423 | }, 424 | { 425 | "class": "CyberEntity", 426 | "value": "sw.product" 427 | }, 428 | { 429 | "class": "Token", 430 | "value": "is" 431 | }, 432 | { 433 | "class": "CyberEntity", 434 | "value": "sw.version" 435 | } 436 | ], 437 | "patternType": "ExactPattern", 438 | "vertexType": "software" 439 | }, 440 | { 441 | "patternSequence": [ 442 | { 443 | "class": "Token", 444 | "value": "versions" 445 | }, 446 | { 447 | "class": "Token", 448 | "value": "of" 449 | }, 450 | { 451 | "class": "CyberEntity", 452 | "value": "sw.product" 453 | }, 454 | { 455 | "class": "Token", 456 | "value": "are" 457 | }, 458 | { 459 | "class": "CyberEntity", 460 | "value": "sw.version" 461 | }, 462 | { 463 | "class": "POS", 464 | "value": "IN" 465 | }, 466 | { 467 | "class": "CyberEntity", 468 | "value": "sw.version" 469 | } 470 | ], 471 | "patternType": "ExactPattern", 472 | "vertexType": "software" 473 | }, 474 | { 475 | "patternSequence": [ 476 | { 477 | "class": "CyberEntity", 478 | "value": "sw.product" 479 | }, 480 | { 481 | "class": "Token", 482 | "value": "to" 483 | }, 484 | { 485 | "class": "CyberEntity", 486 | "value": "sw.version" 487 | } 488 | ], 489 | "patternType": "ExactPattern", 490 | "vertexType": "software" 491 | }, 492 | { 493 | "patternSequence": [ 494 | { 495 | "class": "CyberEntity", 496 | "value": "sw.product" 497 | }, 498 | { 499 | "class": "Token", 500 | "value": "affects" 501 | }, 502 | { 503 | "class": "CyberEntity", 504 | "value": "sw.version" 505 | } 506 | ], 507 | "patternType": "ExactPattern", 508 | "vertexType": "software" 509 | }, 510 | { 511 | "patternSequence": [ 512 | { 513 | "class": "CyberEntity", 514 | "value": "sw.version" 515 | }, 516 | { 517 | "class": "Token", 518 | "value": "of" 519 | }, 520 | { 521 | "class": "CyberEntity", 522 | "value": "sw.product" 523 | } 524 | ], 525 | "patternType": "ExactPattern", 526 | "vertexType": "software" 527 | }, 528 | { 529 | "patternSequence": [ 530 | { 531 | "class": "CyberEntity", 532 | "value": "sw.vendor" 533 | }, 534 | { 535 | "class": "Token", 536 | "value": "products" 537 | }, 538 | { 539 | "class": "Token", 540 | "value": "," 541 | }, 542 | { 543 | "class": "Token", 544 | "value": "including" 545 | }, 546 | { 547 | "class": "CyberEntity", 548 | "value": "sw.product" 549 | }, 550 | { 551 | "class": "Token", 552 | "value": "," 553 | }, 554 | { 555 | "class": "CyberEntity", 556 | "value": "sw.product" 557 | }, 558 | { 559 | "class": "Token", 560 | "value": "," 561 | }, 562 | { 563 | "class": "Token", 564 | "value": "and" 565 | }, 566 | { 567 | "class": "CyberEntity", 568 | "value": "sw.product" 569 | } 570 | ], 571 | "patternType": "ExactPattern", 572 | "vertexType": "software" 573 | }, 574 | { 575 | "patternSequence": [ 576 | { 577 | "class": "CyberEntity", 578 | "value": "sw.vendor" 579 | }, 580 | { 581 | "class": "Token", 582 | "value": "products" 583 | }, 584 | { 585 | "class": "Token", 586 | "value": "," 587 | }, 588 | { 589 | "class": "Token", 590 | "value": "including" 591 | }, 592 | { 593 | "class": "CyberEntity", 594 | "value": "sw.product" 595 | }, 596 | { 597 | "class": "Token", 598 | "value": "," 599 | }, 600 | { 601 | "class": "CyberEntity", 602 | "value": "sw.product" 603 | } 604 | ], 605 | "patternType": "ExactPattern", 606 | "vertexType": "software" 607 | }, 608 | { 609 | "patternSequence": [ 610 | { 611 | "class": "CyberEntity", 612 | "value": "sw.vendor" 613 | }, 614 | { 615 | "class": "Token", 616 | "value": "products" 617 | }, 618 | { 619 | "class": "Token", 620 | "value": "," 621 | }, 622 | { 623 | "class": "Token", 624 | "value": "including" 625 | }, 626 | { 627 | "class": "CyberEntity", 628 | "value": "sw.product" 629 | } 630 | ], 631 | "patternType": "ExactPattern", 632 | "vertexType": "software" 633 | }, 634 | { 635 | "patternSequence": [ 636 | { 637 | "class": "CyberEntity", 638 | "value": "sw.vendor" 639 | }, 640 | { 641 | "class": "Token", 642 | "value": "has" 643 | }, 644 | { 645 | "class": "Token", 646 | "value": "issued" 647 | }, 648 | { 649 | "class": "Token", 650 | "value": "updates" 651 | }, 652 | { 653 | "class": "Token", 654 | "value": "for" 655 | }, 656 | { 657 | "class": "Token", 658 | "value": "its" 659 | }, 660 | { 661 | "class": "CyberEntity", 662 | "value": "sw.version" 663 | }, 664 | { 665 | "class": "Token", 666 | "value": "versions" 667 | }, 668 | { 669 | "class": "Token", 670 | "value": "of" 671 | }, 672 | { 673 | "class": "CyberEntity", 674 | "value": "sw.product" 675 | }, 676 | { 677 | "class": "Token", 678 | "value": "and" 679 | }, 680 | { 681 | "class": "CyberEntity", 682 | "value": "sw.product" 683 | } 684 | ], 685 | "patternType": "ExactPattern", 686 | "vertexType": "software" 687 | }, 688 | { 689 | "patternSequence": [ 690 | { 691 | "class": "CyberEntity", 692 | "value": "sw.vendor" 693 | }, 694 | { 695 | "class": "Token", 696 | "value": "has" 697 | }, 698 | { 699 | "class": "Token", 700 | "value": "issued" 701 | }, 702 | { 703 | "class": "Token", 704 | "value": "updates" 705 | }, 706 | { 707 | "class": "Token", 708 | "value": "for" 709 | }, 710 | { 711 | "class": "Token", 712 | "value": "its" 713 | }, 714 | { 715 | "class": "CyberEntity", 716 | "value": "sw.version" 717 | }, 718 | { 719 | "class": "Token", 720 | "value": "versions" 721 | }, 722 | { 723 | "class": "Token", 724 | "value": "of" 725 | }, 726 | { 727 | "class": "CyberEntity", 728 | "value": "sw.product" 729 | } 730 | ], 731 | "patternType": "ExactPattern", 732 | "vertexType": "software" 733 | }, 734 | { 735 | "patternSequence": [ 736 | { 737 | "class": "CyberEntity", 738 | "value": "sw.product" 739 | }, 740 | { 741 | "class": "Token", 742 | "value": "from" 743 | }, 744 | { 745 | "class": "CyberEntity", 746 | "value": "sw.vendor" 747 | } 748 | ], 749 | "patternType": "ExactPattern", 750 | "vertexType": "software" 751 | }, 752 | { 753 | "patternSequence": [ 754 | { 755 | "class": "CyberEntity", 756 | "value": "sw.vendor" 757 | }, 758 | { 759 | "class": "Token", 760 | "value": "'s" 761 | }, 762 | { 763 | "class": "CyberEntity", 764 | "value": "sw.product" 765 | } 766 | ], 767 | "patternType": "ExactPattern", 768 | "vertexType": "software" 769 | }, 770 | { 771 | "patternSequence": [ 772 | { 773 | "class": "CyberEntity", 774 | "value": "sw.product" 775 | }, 776 | { 777 | "class": "Token", 778 | "value": "are" 779 | }, 780 | { 781 | "class": "Token", 782 | "value": "available" 783 | }, 784 | { 785 | "class": "Token", 786 | "value": "from" 787 | }, 788 | { 789 | "class": "Token", 790 | "value": "the" 791 | }, 792 | { 793 | "class": "CyberEntity", 794 | "value": "sw.vendor" 795 | } 796 | ], 797 | "patternType": "ExactPattern", 798 | "vertexType": "software" 799 | }, 800 | { 801 | "patternSequence": [ 802 | { 803 | "class": "CyberEntity", 804 | "value": "sw.product" 805 | }, 806 | { 807 | "class": "Token", 808 | "value": "issued" 809 | }, 810 | { 811 | "class": "Token", 812 | "value": "a" 813 | }, 814 | { 815 | "class": "Token", 816 | "value": "fix" 817 | }, 818 | { 819 | "class": "Token", 820 | "value": "for" 821 | }, 822 | { 823 | "class": "Token", 824 | "value": "its" 825 | }, 826 | { 827 | "class": "CyberEntity", 828 | "value": "sw.vendor" 829 | } 830 | ], 831 | "patternType": "ExactPattern", 832 | "vertexType": "software" 833 | }, 834 | { 835 | "patternSequence": [ 836 | { 837 | "class": "CyberEntity", 838 | "value": "sw.vendor" 839 | }, 840 | { 841 | "class": "CyberEntity", 842 | "value": "sw.product" 843 | } 844 | ], 845 | "patternType": "ExactPattern", 846 | "vertexType": "software" 847 | }, 848 | { 849 | "patternSequence": [ 850 | { 851 | "class": "CyberEntity", 852 | "value": "sw.vendor" 853 | }, 854 | { 855 | "class": "Token", 856 | "value": "also" 857 | }, 858 | { 859 | "class": "Token", 860 | "value": "released" 861 | }, 862 | { 863 | "class": "Token", 864 | "value": "a" 865 | }, 866 | { 867 | "class": "Token", 868 | "value": "new" 869 | }, 870 | { 871 | "class": "Token", 872 | "value": "version" 873 | }, 874 | { 875 | "class": "Token", 876 | "value": "of" 877 | }, 878 | { 879 | "class": "Token", 880 | "value": "its" 881 | }, 882 | { 883 | "class": "CyberEntity", 884 | "value": "sw.product" 885 | } 886 | ], 887 | "patternType": "ExactPattern", 888 | "vertexType": "software" 889 | }, 890 | { 891 | "patternSequence": [ 892 | { 893 | "class": "CyberEntity", 894 | "value": "sw.vendor" 895 | }, 896 | { 897 | "class": "CyberEntity", 898 | "value": "sw.product" 899 | }, 900 | { 901 | "class": "Token", 902 | "value": "and" 903 | }, 904 | { 905 | "class": "CyberEntity", 906 | "value": "sw.product" 907 | } 908 | ], 909 | "patternType": "ExactPattern", 910 | "vertexType": "software" 911 | }, 912 | { 913 | "patternSequence": [ 914 | { 915 | "class": "CyberEntity", 916 | "value": "sw.vendor" 917 | }, 918 | { 919 | "class": "Token", 920 | "value": "version" 921 | }, 922 | { 923 | "class": "Token", 924 | "value": "of" 925 | }, 926 | { 927 | "class": "CyberEntity", 928 | "value": "sw.product" 929 | } 930 | ], 931 | "patternType": "FuzzyPattern", 932 | "vertexType": "software" 933 | }, 934 | { 935 | "patternSequence": [ 936 | { 937 | "class": "CyberEntity", 938 | "value": "sw.product" 939 | }, 940 | { 941 | "class": "Token", 942 | "value": "fix" 943 | }, 944 | { 945 | "class": "Token", 946 | "value": "available" 947 | }, 948 | { 949 | "class": "Token", 950 | "value": "from" 951 | }, 952 | { 953 | "class": "CyberEntity", 954 | "value": "sw.vendor" 955 | } 956 | ], 957 | "patternType": "FuzzyPattern", 958 | "vertexType": "software" 959 | }, 960 | { 961 | "edgeType": "ExploitTargetRelatedObservable", 962 | "patternSequence": [ 963 | { 964 | "class": "CyberEntity", 965 | "vType": "outV", 966 | "value": "vuln.description" 967 | }, 968 | { 969 | "class": "Token", 970 | "value": "issue" 971 | }, 972 | { 973 | "class": "Token", 974 | "value": "affects" 975 | }, 976 | { 977 | "class": "Token", 978 | "value": "the" 979 | }, 980 | { 981 | "class": "CyberEntity", 982 | "vType": "inV", 983 | "value": "sw.product" 984 | } 985 | ], 986 | "patternType": "ExactPattern" 987 | }, 988 | { 989 | "edgeType": "ExploitTargetRelatedObservable", 990 | "patternSequence": [ 991 | { 992 | "class": "CyberEntity", 993 | "vType": "outV", 994 | "value": "vuln.description" 995 | }, 996 | { 997 | "class": "Token", 998 | "value": "that" 999 | }, 1000 | { 1001 | "class": "Token", 1002 | "value": "is" 1003 | }, 1004 | { 1005 | "class": "Token", 1006 | "value": "hittable" 1007 | }, 1008 | { 1009 | "class": "Token", 1010 | "value": "through" 1011 | }, 1012 | { 1013 | "class": "CyberEntity", 1014 | "vType": "inV", 1015 | "value": "sw.product" 1016 | } 1017 | ], 1018 | "patternType": "ExactPattern" 1019 | }, 1020 | { 1021 | "edgeType": "ExploitTargetRelatedObservable", 1022 | "patternSequence": [ 1023 | { 1024 | "class": "CyberEntity", 1025 | "vType": "inV", 1026 | "value": "sw.product" 1027 | }, 1028 | { 1029 | "class": "Token", 1030 | "value": "vulnerabilities" 1031 | }, 1032 | { 1033 | "class": "Token", 1034 | "value": "that" 1035 | }, 1036 | { 1037 | "class": "Token", 1038 | "value": "could" 1039 | }, 1040 | { 1041 | "class": "Token", 1042 | "value": "lead" 1043 | }, 1044 | { 1045 | "class": "Token", 1046 | "value": "to" 1047 | }, 1048 | { 1049 | "class": "CyberEntity", 1050 | "vType": "outV", 1051 | "value": "vuln.description" 1052 | } 1053 | ], 1054 | "patternType": "ExactPattern" 1055 | }, 1056 | { 1057 | "edgeType": "ExploitTargetRelatedObservable", 1058 | "patternSequence": [ 1059 | { 1060 | "class": "CyberEntity", 1061 | "vType": "inV", 1062 | "value": "sw.product" 1063 | }, 1064 | { 1065 | "class": "CyberEntity", 1066 | "vType": "outV", 1067 | "value": "vuln.description" 1068 | } 1069 | ], 1070 | "patternType": "ExactPattern" 1071 | }, 1072 | { 1073 | "edgeType": "ExploitTargetRelatedObservable", 1074 | "patternSequence": [ 1075 | { 1076 | "class": "CyberEntity", 1077 | "vType": "inV", 1078 | "value": "sw.product" 1079 | }, 1080 | { 1081 | "class": "Token", 1082 | "value": "is" 1083 | }, 1084 | { 1085 | "class": "Token", 1086 | "value": "suffering" 1087 | }, 1088 | { 1089 | "class": "Token", 1090 | "value": "from" 1091 | }, 1092 | { 1093 | "class": "Token", 1094 | "value": "these" 1095 | }, 1096 | { 1097 | "class": "Token", 1098 | "value": "types" 1099 | }, 1100 | { 1101 | "class": "Token", 1102 | "value": "of" 1103 | }, 1104 | { 1105 | "class": "CyberEntity", 1106 | "vType": "outV", 1107 | "value": "vuln.description" 1108 | } 1109 | ], 1110 | "patternType": "ExactPattern" 1111 | }, 1112 | { 1113 | "edgeType": "ExploitTargetRelatedObservable", 1114 | "patternSequence": [ 1115 | { 1116 | "class": "CyberEntity", 1117 | "vType": "inV", 1118 | "value": "sw.product" 1119 | }, 1120 | { 1121 | "class": "Token", 1122 | "value": "could" 1123 | }, 1124 | { 1125 | "class": "Token", 1126 | "value": "allow" 1127 | }, 1128 | { 1129 | "class": "CyberEntity", 1130 | "vType": "outV", 1131 | "value": "vuln.description" 1132 | } 1133 | ], 1134 | "patternType": "ExactPattern" 1135 | }, 1136 | { 1137 | "edgeType": "ExploitTargetRelatedObservable", 1138 | "patternSequence": [ 1139 | { 1140 | "class": "CyberEntity", 1141 | "vType": "inV", 1142 | "value": "sw.product" 1143 | }, 1144 | { 1145 | "class": "Token", 1146 | "value": "." 1147 | }, 1148 | { 1149 | "class": "Token", 1150 | "value": "The" 1151 | }, 1152 | { 1153 | "class": "Token", 1154 | "value": "vulnerabilities" 1155 | }, 1156 | { 1157 | "class": "Token", 1158 | "value": "could" 1159 | }, 1160 | { 1161 | "class": "Token", 1162 | "value": "allow" 1163 | }, 1164 | { 1165 | "class": "CyberEntity", 1166 | "vType": "outV", 1167 | "value": "vuln.description" 1168 | } 1169 | ], 1170 | "patternType": "ExactPattern" 1171 | }, 1172 | { 1173 | "edgeType": "ExploitTargetRelatedObservable", 1174 | "patternSequence": [ 1175 | { 1176 | "class": "CyberEntity", 1177 | "vType": "inV", 1178 | "value": "sw.product" 1179 | }, 1180 | { 1181 | "class": "Token", 1182 | "value": "." 1183 | }, 1184 | { 1185 | "class": "Token", 1186 | "value": "The" 1187 | }, 1188 | { 1189 | "class": "Token", 1190 | "value": "vulnerability" 1191 | }, 1192 | { 1193 | "class": "Token", 1194 | "value": "could" 1195 | }, 1196 | { 1197 | "class": "Token", 1198 | "value": "allow" 1199 | }, 1200 | { 1201 | "class": "CyberEntity", 1202 | "vType": "outV", 1203 | "value": "vuln.description" 1204 | } 1205 | ], 1206 | "patternType": "ExactPattern" 1207 | }, 1208 | { 1209 | "edgeType": "ExploitTargetRelatedObservable", 1210 | "patternSequence": [ 1211 | { 1212 | "class": "CyberEntity", 1213 | "vType": "inV", 1214 | "value": "sw.product" 1215 | }, 1216 | { 1217 | "class": "Token", 1218 | "value": "." 1219 | }, 1220 | { 1221 | "class": "Token", 1222 | "value": "The" 1223 | }, 1224 | { 1225 | "class": "Token", 1226 | "value": "most" 1227 | }, 1228 | { 1229 | "class": "Token", 1230 | "value": "severe" 1231 | }, 1232 | { 1233 | "class": "Token", 1234 | "value": "vulnerabilities" 1235 | }, 1236 | { 1237 | "class": "Token", 1238 | "value": "could" 1239 | }, 1240 | { 1241 | "class": "Token", 1242 | "value": "allow" 1243 | }, 1244 | { 1245 | "class": "CyberEntity", 1246 | "vType": "outV", 1247 | "value": "vuln.description" 1248 | } 1249 | ], 1250 | "patternType": "ExactPattern" 1251 | }, 1252 | { 1253 | "edgeType": "ExploitTargetRelatedObservable", 1254 | "patternSequence": [ 1255 | { 1256 | "class": "CyberEntity", 1257 | "vType": "outV", 1258 | "value": "vuln.description" 1259 | }, 1260 | { 1261 | "class": "Token", 1262 | "value": "vulnerability" 1263 | }, 1264 | { 1265 | "class": "Token", 1266 | "value": "in" 1267 | }, 1268 | { 1269 | "class": "CyberEntity", 1270 | "vType": "inV", 1271 | "value": "sw.product" 1272 | } 1273 | ], 1274 | "patternType": "ExactPattern" 1275 | }, 1276 | { 1277 | "edgeType": "ExploitTargetRelatedObservable", 1278 | "patternSequence": [ 1279 | { 1280 | "class": "CyberEntity", 1281 | "vType": "outV", 1282 | "value": "vuln.description" 1283 | }, 1284 | { 1285 | "class": "Token", 1286 | "value": "vulnerability" 1287 | }, 1288 | { 1289 | "class": "Token", 1290 | "value": "in" 1291 | }, 1292 | { 1293 | "class": "CyberEntity", 1294 | "vType": "inV", 1295 | "value": "sw.product" 1296 | }, 1297 | { 1298 | "class": "Token", 1299 | "value": "and" 1300 | }, 1301 | { 1302 | "class": "CyberEntity", 1303 | "vType": "inV", 1304 | "value": "sw.product" 1305 | } 1306 | ], 1307 | "patternType": "ExactPattern" 1308 | }, 1309 | { 1310 | "edgeType": "ExploitTargetRelatedObservable", 1311 | "patternSequence": [ 1312 | { 1313 | "class": "CyberEntity", 1314 | "vType": "inV", 1315 | "value": "sw.product" 1316 | }, 1317 | { 1318 | "class": "Token", 1319 | "value": "that" 1320 | }, 1321 | { 1322 | "class": "Token", 1323 | "value": "may" 1324 | }, 1325 | { 1326 | "class": "Token", 1327 | "value": "lead" 1328 | }, 1329 | { 1330 | "class": "Token", 1331 | "value": "to" 1332 | }, 1333 | { 1334 | "class": "CyberEntity", 1335 | "vType": "outV", 1336 | "value": "vuln.description" 1337 | } 1338 | ], 1339 | "patternType": "ExactPattern" 1340 | }, 1341 | { 1342 | "edgeType": "ExploitTargetRelatedObservable", 1343 | "patternSequence": [ 1344 | { 1345 | "class": "CyberEntity", 1346 | "vType": "inV", 1347 | "value": "sw.product" 1348 | }, 1349 | { 1350 | "class": "Token", 1351 | "value": "is" 1352 | }, 1353 | { 1354 | "class": "Token", 1355 | "value": "vulnerable" 1356 | }, 1357 | { 1358 | "class": "Token", 1359 | "value": "to" 1360 | }, 1361 | { 1362 | "class": "CyberEntity", 1363 | "vType": "outV", 1364 | "value": "vuln.description" 1365 | } 1366 | ], 1367 | "patternType": "FuzzyPattern" 1368 | }, 1369 | { 1370 | "edgeType": "ExploitTargetRelatedObservable", 1371 | "patternSequence": [ 1372 | { 1373 | "class": "CyberEntity", 1374 | "vType": "outV", 1375 | "value": "vuln.description" 1376 | }, 1377 | { 1378 | "class": "Token", 1379 | "value": "flaw" 1380 | }, 1381 | { 1382 | "class": "Token", 1383 | "value": "in" 1384 | }, 1385 | { 1386 | "class": "CyberEntity", 1387 | "vType": "inV", 1388 | "value": "sw.product" 1389 | } 1390 | ], 1391 | "patternType": "ExactPattern" 1392 | }, 1393 | { 1394 | "edgeType": "Sub-Observable", 1395 | "patternSequence": [ 1396 | { 1397 | "class": "CyberEntity", 1398 | "vType": "outV", 1399 | "value": "sw.product" 1400 | }, 1401 | { 1402 | "class": "Token", 1403 | "value": "system" 1404 | }, 1405 | { 1406 | "class": "Token", 1407 | "value": "file" 1408 | }, 1409 | { 1410 | "class": "Token", 1411 | "value": ":" 1412 | }, 1413 | { 1414 | "class": "CyberEntity", 1415 | "vType": "inV", 1416 | "value": "file.name" 1417 | } 1418 | ], 1419 | "patternType": "ExactPattern" 1420 | }, 1421 | { 1422 | "edgeType": "Sub-Observable", 1423 | "patternSequence": [ 1424 | { 1425 | "class": "CyberEntity", 1426 | "vType": "outV", 1427 | "value": "sw.product" 1428 | }, 1429 | { 1430 | "class": "POS", 1431 | "value": "NN" 1432 | }, 1433 | { 1434 | "class": "Token", 1435 | "value": "-LRB-" 1436 | }, 1437 | { 1438 | "class": "CyberEntity", 1439 | "vType": "inV", 1440 | "value": "file.name" 1441 | } 1442 | ], 1443 | "patternType": "ExactPattern" 1444 | } 1445 | ] 1446 | } 1447 | -------------------------------------------------------------------------------- /src/main/resources/patterns_relations_abbrev.json: -------------------------------------------------------------------------------- 1 | { 2 | "Patterns": [ 3 | { 4 | "patternSequence": [ 5 | { 6 | "class": "CyberEntity", 7 | "value": "sw.vendor" 8 | }, 9 | { 10 | "class": "CyberEntity", 11 | "value": "sw.product" 12 | }, 13 | { 14 | "class": "CyberEntity", 15 | "value": "sw.version" 16 | } 17 | ], 18 | "patternType": "ExactPattern", 19 | "vertexType": "software" 20 | }, 21 | { 22 | "edgeType": "ExploitTargetRelatedObservable", 23 | "patternSequence": [ 24 | { 25 | "class": "CyberEntity", 26 | "vType": "inV", 27 | "value": "file.name" 28 | }, 29 | { 30 | "class": "Token", 31 | "value": "refer" 32 | }, 33 | { 34 | "class": "Token", 35 | "value": "to" 36 | }, 37 | { 38 | "class": "CyberEntity", 39 | "vType": "outV", 40 | "value": "vuln.cve" 41 | } 42 | ], 43 | "patternType": "FuzzyPattern" 44 | }, 45 | { 46 | "vertexType": "software", 47 | "patternSequence": [ 48 | { 49 | "class": "CyberEntity", 50 | "value": "sw.vendor" 51 | }, 52 | { 53 | "class": "TreeElement", 54 | "value": "NNP" 55 | }, 56 | { 57 | "class": "TreeElement", 58 | "value": "NP" 59 | }, 60 | { 61 | "class": "CyberEntity", 62 | "value": "sw.product" 63 | } 64 | ], 65 | "patternType": "ParseTreePattern" 66 | }, 67 | { 68 | "vertexType": "software", 69 | "patternSequence": [ 70 | { 71 | "class": "CyberEntity", 72 | "value": "sw.product" 73 | }, 74 | { 75 | "class": "TreeElement", 76 | "value": "NP" 77 | }, 78 | { 79 | "class": "TreeElement", 80 | "value": "NP" 81 | }, 82 | { 83 | "class": "TreeElement", 84 | "value": "PP" 85 | }, 86 | { 87 | "class": "CyberEntity", 88 | "value": "sw.version" 89 | } 90 | ], 91 | "patternType": "ParseTreePattern" 92 | }, 93 | { 94 | "edgeType": "ExploitTargetRelatedObservable", 95 | "patternSequence": [ 96 | { 97 | "class": "CyberEntity", 98 | "vType": "inV", 99 | "value": "sw.product" 100 | }, 101 | { 102 | "class": "TreeElement", 103 | "value": "NP" 104 | }, 105 | { 106 | "class": "TreeElement", 107 | "value": "NP" 108 | }, 109 | { 110 | "class": "TreeElement", 111 | "value": "S" 112 | }, 113 | { 114 | "class": "TreeElement", 115 | "value": "VP" 116 | }, 117 | { 118 | "class": "TreeElement", 119 | "value": "NP" 120 | }, 121 | { 122 | "class": "TreeElement", 123 | "value": "NP" 124 | }, 125 | { 126 | "class": "CyberEntity", 127 | "vType": "outV", 128 | "value": "vuln.description" 129 | } 130 | ], 131 | "patternType": "ParseTreePattern" 132 | }, 133 | { 134 | "edgeType": "ExploitTargetRelatedObservable", 135 | "patternSequence": [ 136 | { 137 | "class": "CyberEntity", 138 | "vType": "inV", 139 | "value": "sw.product" 140 | }, 141 | { 142 | "class": "TreeElement", 143 | "value": "NP" 144 | }, 145 | { 146 | "class": "TreeElement", 147 | "value": "NP" 148 | }, 149 | { 150 | "class": "TreeElement", 151 | "value": "S" 152 | }, 153 | { 154 | "class": "TreeElement", 155 | "value": "VP" 156 | }, 157 | { 158 | "class": "TreeElement", 159 | "value": "NP" 160 | }, 161 | { 162 | "class": "TreeElement", 163 | "value": "PP" 164 | }, 165 | { 166 | "class": "TreeElement", 167 | "value": "NP" 168 | }, 169 | { 170 | "class": "TreeElement", 171 | "value": "PRN" 172 | }, 173 | { 174 | "class": "TreeElement", 175 | "value": "S" 176 | }, 177 | { 178 | "class": "TreeElement", 179 | "value": "VP" 180 | }, 181 | { 182 | "class": "TreeElement", 183 | "value": "PP" 184 | }, 185 | { 186 | "class": "TreeElement", 187 | "value": "NP" 188 | }, 189 | { 190 | "class": "TreeElement", 191 | "value": "NN" 192 | }, 193 | { 194 | "class": "CyberEntity", 195 | "vType": "outV", 196 | "value": "vuln.cve" 197 | } 198 | ], 199 | "patternType": "ParseTreePattern" 200 | }, 201 | { 202 | "edgeType": "Sub-Observable", 203 | "patternSequence": [ 204 | { 205 | "class": "CyberEntity", 206 | "vType": "outV", 207 | "value": "sw.product" 208 | }, 209 | { 210 | "class": "TreeElement", 211 | "value": "NP" 212 | }, 213 | { 214 | "class": "TreeElement", 215 | "value": "NP" 216 | }, 217 | { 218 | "class": "TreeElement", 219 | "value": "S" 220 | }, 221 | { 222 | "class": "TreeElement", 223 | "value": "VP" 224 | }, 225 | { 226 | "class": "TreeElement", 227 | "value": "NP" 228 | }, 229 | { 230 | "class": "TreeElement", 231 | "value": "PP" 232 | }, 233 | { 234 | "class": "TreeElement", 235 | "value": "NP" 236 | }, 237 | { 238 | "class": "TreeElement", 239 | "value": "NP" 240 | }, 241 | { 242 | "class": "TreeElement", 243 | "value": "NN" 244 | }, 245 | { 246 | "class": "CyberEntity", 247 | "vType": "inV", 248 | "value": "file.name" 249 | } 250 | ], 251 | "patternType": "ParseTreePattern" 252 | } 253 | ] 254 | } 255 | -------------------------------------------------------------------------------- /src/test/java/gov/ornl/stucco/RelationExtractorTest.java: -------------------------------------------------------------------------------- 1 | package gov.ornl.stucco; 2 | 3 | import edu.stanford.nlp.pipeline.Annotation; 4 | import gov.ornl.stucco.entity.EntityLabeler; 5 | 6 | import org.junit.Test; 7 | 8 | public class RelationExtractorTest { 9 | private static String expectedGraph = "{" + 10 | "\"vertices\": {" + 11 | "\"1235\": {" + 12 | "\"name\": \"1235\"," + 13 | "\"vertexType\": \"software\"," + 14 | "\"product\": \"Windows XP\"," + 15 | "\"source\": \"CNN\"," + 16 | "\"vendor\": \"Microsoft\"," + 17 | "\"version\": \"before 2.8\"" + 18 | "}," + 19 | "\"1236\": {" + 20 | "\"name\": \"file.php\"," + 21 | "\"vertexType\": \"file\"," + 22 | "\"source\": \"CNN\"" + 23 | "}," + 24 | "\"1237\": {" + 25 | "\"name\": \"1237\"," + 26 | "\"vertexType\": \"vulnerability\"," + 27 | "\"source\": \"CNN\"," + 28 | "\"cve\": \"CVE-2014-1234\"" + 29 | "}," + 30 | "\"1238\": {" + 31 | "\"name\": \"1238\"," + 32 | "\"vertexType\": \"software\"," + 33 | "\"product\": \"Windows XP\"," + 34 | "\"source\": \"CNN\"," + 35 | "\"vendor\": \"Microsoft\"" + 36 | "}," + 37 | "\"1239\": {" + 38 | "\"name\": \"1239\"," + 39 | "\"vertexType\": \"software\"," + 40 | "\"product\": \"Windows XP\"," + 41 | "\"source\": \"CNN\"," + 42 | "\"version\": \"before 2.8\"" + 43 | "}," + 44 | "\"1240\": {" + 45 | "\"name\": \"1240\"," + 46 | "\"vertexType\": \"software\"," + 47 | "\"product\": \"Windows XP\"," + 48 | "\"source\": \"CNN\"" + 49 | "}," + 50 | "\"1241\": {" + 51 | "\"name\": \"1241\"," + 52 | "\"vertexType\": \"vulnerability\"," + 53 | "\"source\": \"CNN\"," + 54 | "\"description\": \"cross-site scripting\"" + 55 | "}," + 56 | "\"1242\": {" + 57 | "\"name\": \"1242\"," + 58 | "\"vertexType\": \"software\"," + 59 | "\"product\": \"Windows XP\"," + 60 | "\"source\": \"CNN\"" + 61 | "}," + 62 | "\"1243\": {" + 63 | "\"name\": \"1243\"," + 64 | "\"vertexType\": \"vulnerability\"," + 65 | "\"source\": \"CNN\"," + 66 | "\"cve\": \"CVE-2014-1234\"" + 67 | "}," + 68 | "\"1244\": {" + 69 | "\"name\": \"1244\"," + 70 | "\"vertexType\": \"software\"," + 71 | "\"product\": \"Windows XP\"," + 72 | "\"source\": \"CNN\"" + 73 | "}," + 74 | "\"1245\": {" + 75 | "\"name\": \"file.php\"," + 76 | "\"vertexType\": \"file\"," + 77 | "\"source\": \"CNN\"" + 78 | "}" + 79 | "}," + 80 | "\"edges\": [" + 81 | "{" + 82 | "\"inVertID\": \"1236\"," + 83 | "\"outVertID\": \"1237\"," + 84 | "\"relation\": \"ExploitTargetRelatedObservable\"" + 85 | "}," + 86 | "{" + 87 | "\"inVertID\": \"1240\"," + 88 | "\"outVertID\": \"1241\"," + 89 | "\"relation\": \"ExploitTargetRelatedObservable\"" + 90 | "}," + 91 | "{" + 92 | "\"inVertID\": \"1242\"," + 93 | "\"outVertID\": \"1243\"," + 94 | "\"relation\": \"ExploitTargetRelatedObservable\"" + 95 | "}," + 96 | "{" + 97 | "\"inVertID\": \"1245\"," + 98 | "\"outVertID\": \"1244\"," + 99 | "\"relation\": \"Sub-Observable\"" + 100 | "}" + 101 | "]" + 102 | "}"; 103 | 104 | @Test 105 | public void testGetGraph() { 106 | String testSentence = "Microsoft Windows XP before 2.8 has cross-site scripting vulnerability in file.php (refer to CVE-2014-1234)."; 107 | EntityLabeler labeler = new EntityLabeler(); 108 | Annotation doc = labeler.getAnnotatedDoc("My Doc", testSentence); 109 | RelationExtractor rx = new RelationExtractor("src/main/resources/patterns_relations_abbrev.json"); 110 | String graph = rx.createSubgraph(doc, "a source"); 111 | assert(graph.equals(expectedGraph)); 112 | } 113 | 114 | } 115 | --------------------------------------------------------------------------------