public class AttackPathManagement {

    /**
     * Extract
     *
     * @param attackGraph the attack graph
     * @return the scores extracted from the attack graph in the information system
     */
    public static List<AttackPath> scoreAttackPaths(AttackGraph attackGraph, double previousMaxScore) {
        try {
            String outputFolderPath = ProjectProperties.getProperty("output-path");
            attackGraph.saveToXmlFile(outputFolderPath + "/attack-graph-to-score.xml");
            return attackGraph.scoreAttackGraphAndGetAttackPaths(outputFolderPath + "/scored-attack-paths.xml", previousMaxScore);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * @param monitoring a monitoring object
     * @return the XML element related to all the attack paths contained in the monitoring object
     */
    public static Element getAttackPathsXML(Monitoring monitoring) {
        if (monitoring == null)
            return null;
        List<AttackPath> attackPaths = monitoring.getAttackPathList();
        Element root = new Element("attack_paths");
        for (AttackPath attackPath : attackPaths) {
            Element element = attackPath.toDomXMLElement();
            root.addContent(element);
        }

        return root;
    }

    /**
     * @param monitoring a monitoring object
     * @param id         the id of the attack path to print
     * @return the XML element related to the selected attack path contained in the monitoring object
     */
    public static Element getAttackPathXML(Monitoring monitoring, Integer id) {
        if (monitoring == null)
            return null;
        List<AttackPath> attackPaths = monitoring.getAttackPathList();

        if (id >= 0 && id < attackPaths.size()) {
            return attackPaths.get(id).toDomXMLElement();
        }
        return null;
    }

    /**
     * @param monitoring a monitoring object
     * @param id         the id of the attack path to print
     * @return the JSON element related to the topological attack path contained in the monitoring object
     */
    public static JSONObject getAttackPathTopologicalJson(Monitoring monitoring, Integer id) {
        if (monitoring == null)
            throw new IllegalStateException("The monitoring object is null");
        List<AttackPath> attackPaths = monitoring.getAttackPathList();

        if (id >= 0 && id < attackPaths.size()) {
            AttackPath attackPath = attackPaths.get(id);
            try {
                return attackPath.getRelatedTopologyGraph(monitoring.getInformationSystem()).toJsonObject();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        throw new IllegalStateException("This attack path can not be found.");
    }

    /**
     * @param monitoring a monitoring object
     * @return the JSON element related to the topological attack graph contained in the monitoring object
     */
    public static JSONObject getAttackGraphTopologicalJson(Monitoring monitoring) {
        if (monitoring == null)
            throw new IllegalStateException("The monitoring object is null");
        AttackGraph attackGraph = monitoring.getAttackGraph();

        try {
            return attackGraph.getRelatedTopologyGraph(monitoring.getInformationSystem()).toJsonObject();
        } catch (Exception e) {
            e.printStackTrace();
        }

        throw new IllegalStateException("This attack path can not be found.");
    }

    /**
     * @param monitoring a monitoring object
     * @param id         the id of the attack path to remediate
     * @param db         the remediation database
     * @return the XML element related to the remediations to the selected attack path contained in the monitoring ojbect
     */
    public static Element getRemediationXML(Monitoring monitoring, Integer id, Database db) {
        if (monitoring == null)
            return null;
        List<AttackPath> attackPaths = monitoring.getAttackPathList();
        if (id >= 0 && id < attackPaths.size()) {
            AttackPath attackPath = attackPaths.get(id);
            try {
                List<DeployableRemediation> remediations = attackPath.getDeployableRemediations(monitoring.getInformationSystem(), db.getConn(), monitoring.getPathToCostParametersFolder());
                Element root = new Element("remediations");
                for (DeployableRemediation remediation : remediations) {
                    root.addContent(remediation.toXMLElement());
                }
                return root;
            } catch (Exception e) {
                System.err.println("Error while computing remediations");
                e.printStackTrace();
            }
        }
        return null;
    }
} public class InformationSystemManagement {
    /**
     * Load the information system from an XML File
     *
     * @param XMLInformationSystemFile the XML file path
     * @return the InformationSystem object
     */
    public static InformationSystem loadTopologyXMLFile(String XMLInformationSystemFile, Database db) {

        InformationSystem result = new InformationSystem();
        try {
            result.loadFromXMLFile(XMLInformationSystemFile, db);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Execute MulVAL on the topology and return the attack graph
     *
     * @param informationSystem the input network
     * @return the associated attack graph object
     */
    public static AttackGraph prepareInputsAndExecuteMulVal(InformationSystem informationSystem) {
        if (informationSystem == null)
            return null;
        try {
            //Load MulVAL properties

            String mulvalPath = ProjectProperties.getProperty("mulval-path");
            String xsbPath = ProjectProperties.getProperty("xsb-path");
            String outputFolderPath = ProjectProperties.getProperty("output-path");

            File mulvalInputFile = new File(ProjectProperties.getProperty("mulval-input"));

            File mulvalOutputFile = new File(outputFolderPath + "/AttackGraph.xml");
            if (mulvalOutputFile.exists()) {
                mulvalOutputFile.delete();
            }
            Logger.getAnonymousLogger().log(Level.INFO, "Genering MulVAL inputs");
            informationSystem.exportToMulvalDatalogFile(mulvalInputFile.getAbsolutePath());

            Logger.getAnonymousLogger().log(Level.INFO, "Launching MulVAL");
            ProcessBuilder processBuilder = new ProcessBuilder(mulvalPath + "/utils/graph_gen.sh", mulvalInputFile.getAbsolutePath(), "-l");

            if (ProjectProperties.getProperty("mulval-rules-path") != null) {
                processBuilder.command().add("-r");
                processBuilder.command().add(ProjectProperties.getProperty("mulval-rules-path"));
            }

            processBuilder.directory(new File(outputFolderPath));
            processBuilder.environment().put("MULVALROOT", mulvalPath);
            String path = System.getenv("PATH");
            processBuilder.environment().put("PATH", mulvalPath + "/utils/:" + xsbPath + ":" + path);
            Process process = processBuilder.start();
            process.waitFor();

            if (!mulvalOutputFile.exists()) {
                Logger.getAnonymousLogger().log(Level.INFO, "Empty attack graph!");
                return null;
            }

            MulvalAttackGraph ag = new MulvalAttackGraph(mulvalOutputFile.getAbsolutePath());

            ag.loadMetricsFromTopology(informationSystem);

            return ag;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Execute MulVAL on the topology and return the attack graph
     *
     * @return the associated attack graph object
     */
    public static AttackGraph generateAttackGraphWithMulValUsingAlreadyGeneratedMulVALInputFile() {
        try {
            //Load MulVAL properties

            String mulvalPath = ProjectProperties.getProperty("mulval-path");
            String xsbPath = ProjectProperties.getProperty("xsb-path");
            String outputFolderPath = ProjectProperties.getProperty("output-path");

            File mulvalInputFile = new File(ProjectProperties.getProperty("mulval-input"));

            File mulvalOutputFile = new File(outputFolderPath + "/AttackGraph.xml");
            if (mulvalOutputFile.exists()) {
                mulvalOutputFile.delete();
            }

            Logger.getAnonymousLogger().log(Level.INFO, "Launching MulVAL");
            ProcessBuilder processBuilder = new ProcessBuilder(mulvalPath + "/utils/graph_gen.sh", mulvalInputFile.getAbsolutePath(), "-l");

            if (ProjectProperties.getProperty("mulval-rules-path") != null) {
                processBuilder.command().add("-r");
                processBuilder.command().add(ProjectProperties.getProperty("mulval-rules-path"));
            }

            processBuilder.directory(new File(outputFolderPath));
            processBuilder.environment().put("MULVALROOT", mulvalPath);
            String path = System.getenv("PATH");
            processBuilder.environment().put("PATH", mulvalPath + "/utils/:" + xsbPath + ":" + path);
            Process process = processBuilder.start();
            process.waitFor();

            if (!mulvalOutputFile.exists()) {
                Logger.getAnonymousLogger().log(Level.INFO, "Empty attack graph!");
                return null;
            }

            MulvalAttackGraph ag = new MulvalAttackGraph(mulvalOutputFile.getAbsolutePath());

            return ag;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * @param informationSystem The initial topology
     * @param remediation       the remediation deployment that must be simulated
     * @param db                the vulnerability database
     * @return a clone of the topology, in which the remediation is applied
     */
    public static InformationSystem simulateRemediationOnNewInforationSystem(InformationSystem informationSystem, DeployableRemediation remediation, Database db) {
        InformationSystem simulatedTopology = null;
        try {
            simulatedTopology = informationSystem.clone();

            for (int i = 0; i < remediation.getActions().size(); i++) {
                DeployableRemediationAction action = remediation.getActions().get(i);
                Logger.getAnonymousLogger().log(Level.INFO, "Simulate the remediation " + action.getRemediationAction().getActionType() + " on machine " + action.getHost());
                switch (action.getRemediationAction().getActionType()) {
                    case APPLY_PATCH:
                        for (int j = 0; j < action.getRemediationAction().getRemediationParameters().size(); j++) {
                            Patch patch = (Patch) action.getRemediationAction().getRemediationParameters().get(j);
                            List<Vulnerability> correctedVulnerabilities = patch.getCorectedVulnerabilities(db.getConn());
                            simulatedTopology.existingMachineByNameOrIPAddress(action.getHost().getName()).correctVulnerabilities(correctedVulnerabilities);
                        }
                        break;
                    case DEPLOY_FIREWALL_RULE:
                        FirewallRule rule = (FirewallRule) action.getRemediationAction().getRemediationParameters().get(0);
                        if (rule.getTable() == FirewallRule.Table.INPUT) {
                            simulatedTopology.existingMachineByNameOrIPAddress(action.getHost().getName()).getInputFirewallRulesTable().getRuleList().add(0, rule);
                        } else if (rule.getTable() == FirewallRule.Table.OUTPUT) {
                            simulatedTopology.existingMachineByNameOrIPAddress(action.getHost().getName()).getOutputFirewallRulesTable().getRuleList().add(0, rule);
                        }
                        break;
                    case DEPLOY_SNORT_RULE:
                        for (int j = 0; j < action.getRemediationAction().getRemediationParameters().size(); j++) {
                            Rule snortRule = (Rule) action.getRemediationAction().getRemediationParameters().get(j);
                            List<Vulnerability> correctedVulnerabilities = snortRule.getCorectedVulnerabilities(db.getConn());
                            //TODO: In fact, the vulnerability is not really corrected but rather suppressed on the path...
                            simulatedTopology.existingMachineByNameOrIPAddress(action.getRemediationAction().getRelatedVertex().concernedMachine.getName()).correctVulnerabilities(correctedVulnerabilities);
                        }
                        break;
                    default:
                        break;
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return simulatedTopology;
    }

    /**
     * Execute the python script that builds MulVAL inputs
     *
     * @return boolean true if the execution was right
     */
    public static boolean prepareMulVALInputs() {
        try {
            //Load python script properties

            String pythonPath = ProjectProperties.getProperty("python-path");
            String mulvalInputScriptFolder = ProjectProperties.getProperty("mulval-input-script-folder");
            String mulvalInputScriptPath = mulvalInputScriptFolder + "main.py";

            String hostInterfacePath = ProjectProperties.getProperty("host-interfaces-path");
            String vlansPath = ProjectProperties.getProperty("vlans-path");
            String routingPath = ProjectProperties.getProperty("routing-path");
            String flowMatrixPath = ProjectProperties.getProperty("flow-matrix-path");
            String vulnerabilityScanPath = ProjectProperties.getProperty("vulnerability-scan-path");
            String mulvalInputPath = ProjectProperties.getProperty("mulval-input");
            String topologyPath = ProjectProperties.getProperty("topology-path");


            File mulvalInputFile = new File(mulvalInputPath);
            if (mulvalInputFile.exists()) {
                mulvalInputFile.delete();
            }

            Logger.getAnonymousLogger().log(Level.INFO, "Genering MulVAL inputs");

            //TODO: use parameter nessus-files-path rather than vulnerability-scan-path, in order to manage when
            // mutliple nessus files are provided.
            ProcessBuilder processBuilder = new ProcessBuilder(pythonPath, mulvalInputScriptPath,
                    "--hosts-interfaces-file", hostInterfacePath,
                    "--vlans-file", vlansPath,
                    "--flow-matrix-file", flowMatrixPath,
                    "--vulnerability-scan", vulnerabilityScanPath,
                    "--routing-file", routingPath,
                    "--mulval-output-file", mulvalInputFile.getAbsolutePath(),
                    "--to-fiware-xml-topology", topologyPath
            );
            processBuilder.directory(new File(mulvalInputScriptFolder));
            StringBuilder command = new StringBuilder();
            for (String str : processBuilder.command())
                command.append(str + " ");
            Logger.getAnonymousLogger().log(Level.INFO, "Launch generation of MulVAL inputs with command : \n" + command.toString());
            processBuilder.redirectOutput(new File(ProjectProperties.getProperty("output-path") + "/input-generation.log"));
            processBuilder.redirectError(new File(ProjectProperties.getProperty("output-path") + "/input-generation.log"));
            Process process = processBuilder.start();
            process.waitFor();


            if (!mulvalInputFile.exists()) {
                Logger.getAnonymousLogger().log(Level.WARNING, "A problem happened in the generation of mulval inputs");
                return false;
            }

            return true;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * Load the json of hosts with security requirements
     *
     * @param monitoring
     * @param hostsJson
     */
    public static void loadHostsSecurityRequirementsFromJson(Monitoring monitoring, JSONObject hostsJson) throws Exception {
        JSONArray hostsJsonArray = hostsJson.getJSONArray("hosts");
        for (int i = 0; i < hostsJsonArray.length(); i++) {
            JSONObject hostJson = hostsJsonArray.getJSONObject(i);
            if (hostJson != null) {
                String hostName = hostJson.getString("name");
                InformationSystemHost host = monitoring.getInformationSystem().getHostByNameOrIPAddress(hostName);
                if (host == null)
                    throw new IllegalStateException("The host " + hostName + " can not be found ");

                host.removeAllSecurityRequirements();

                JSONArray securityRequirementsArray = hostJson.getJSONArray("security_requirements");
                for (int j = 0; j < securityRequirementsArray.length(); j++) {
                    JSONObject securityRequirementJson = securityRequirementsArray.getJSONObject(j);
                    SecurityRequirement securityRequirement = new SecurityRequirement(securityRequirementJson.getString("name"), SecurityRequirement.getMetricValueFromPlainText(securityRequirementJson.getString("metric")));
                    host.addSecurityRequirements(securityRequirement);
                }

            }
        }
    }
} public class AttackGraph implements Cloneable {
    /**
     * A list of vertices
     */
    public HashMap<Integer, Vertex> vertices = new HashMap<Integer, Vertex>();

    /**
     * A list of arcs between the vertices
     */
    public ArrayList<Arc> arcs = new ArrayList<Arc>();

    /**
     * The global score of the attack graph
     */
    public double globalScore = 0;

    /**
     * Check if a vertex exists, if it doesn't, creates a new one
     *
     * @param id_vertex the id of the vertex to check
     * @return a vertex of identifier id_vertex
     */
    public Vertex getExistingOrCreateVertex(int id_vertex) {
        Vertex result = vertices.get(id_vertex);
        if (result == null) {
            result = new Vertex(id_vertex);
            vertices.put(id_vertex, result);
        }
        return result;
    }

    /**
     * Compute the parents and the children of the whole attack graph
     */
    public void computeAllParentsAndChildren() {
        for (Integer key : vertices.keySet()) {
            vertices.get(key).computeParentsAndChildren(this);
        }
    }

    /**
     * @param id the identifier of the vertex in the attack graph
     * @return the vertex from the attack graph
     * @throws Exception
     */
    public Vertex getVertexFromId(int id) throws Exception {
        Vertex vertex = this.vertices.get(id);
        if (vertex == null)
            throw new Exception("The vertex " + id + " is not in this attack graph");
        return vertex;
    }

    /**
     * Simulate the deletion of a vertex and propagate this deletion on the whole attack graph
     *
     * @param vertex The vertex to delete
     * @throws Exception
     */
    public void deleteVertex(Vertex vertex) throws Exception {
        if (!this.vertices.containsKey(vertex.id)) //If the vertex has already been deleted
            return;

        //In all case (AND, OR and LEAF), we delete the vertex
        this.vertices.remove(vertex.id);

        vertex.computeParentsAndChildren(this);

        for (int i = 0; i < vertex.children.size(); i++) {
            Vertex child = vertex.children.get(i);

            deleteArc(vertex, child); //We delete the arc from this vertex to the child
            if (child.type == VertexType.AND) //If the child is an "AND" he must be remove
                deleteVertex(child);
            else if (child.type == VertexType.OR) { //If the child is an "OR" he must be remove only if it was it last parent

                child.computeParentsAndChildren(this);

                if (child.parents.size() == 0) { //If it is the last parent

                    deleteVertex(child);
                }
            }
        }

        for (int i = 0; i < vertex.parents.size(); i++) { //We delete all vertices from the parents to the vertex
            Vertex parent = vertex.parents.get(i);
            parent.computeParentsAndChildren(this);
            if (parent.children.size() == 1 && parent.children.get(0) == vertex && parent.parents.size() == 0) {//We delete a parent if he has no child remaining and no parents
                deleteVertex(parent);
            }

            deleteArc(parent, vertex);


        }

        this.deleteUnreachableVertices();
    }

    /**
     * Delete all arcs from a vertex to another
     *
     * @param fromVertex the source vertex
     * @param toVertex   the destination vertex
     */
    public void deleteArc(Vertex fromVertex, Vertex toVertex) {
        for (int i = 0; i < this.arcs.size(); i++) {
            Arc arc = this.arcs.get(i);
            if (arc.source.id == fromVertex.id && arc.destination.id == toVertex.id) {
                this.arcs.remove(i);
            }
        }
    }

    /**
     * @return the number of vertices in the attack graph
     */
    public int getNumberOfVertices() {
        return this.vertices.size();
    }

    /**
     * @param vuln a vulnerability
     * @return the vertices that have the vuln vulnerability
     */
    public List<Vertex> getVerticesByVulnerability(Vulnerability vuln) {
        List<Vertex> result = new ArrayList<Vertex>();
        for (Integer key : vertices.keySet()) {
            Vertex vertex = vertices.get(key);
            if (vertex.relatedVulnerabilibty != null && vertex.relatedVulnerabilibty.equals(vuln))
                result.add(vertices.get(key));

        }

        return result;
    }

    /**
     * @return the adjacency matrix related to the attack graph
     */
    public int[][] getAdjacencyMatrix() {
        int numberOfVertices = this.getNumberOfVertices();
        int[][] adjacencyMatrix = new int[numberOfVertices][numberOfVertices];
        for (Arc arc : this.arcs) {
            adjacencyMatrix[arc.source.id - 1][arc.destination.id - 1] = 1;
        }
        return adjacencyMatrix;
    }

    /**
     * Use the taboo method to find all the attack paths between the vertex v1 and v2
     *
     * @param v1 the source vertex
     * @param v2 the destination vertex
     * @return the list of all attacks path from v1 to v2
     * @throws Exception
     */
    public List<List<Vertex>> getExistingAttackPathsBetween(Vertex v1, Vertex v2) throws Exception {
        List<List<Vertex>> attackPathsList = new ArrayList<List<Vertex>>();
        int numberOfVertices = this.getHighestVertexId();
        int[] path = new int[numberOfVertices];
        boolean[] taboo = new boolean[numberOfVertices];
        int source = v1.id - 1;
        int destination = v2.id - 1;
        int[][] adjacencyMatrix = getAdjacencyMatrix();

        explore(attackPathsList, path, destination, taboo, adjacencyMatrix, numberOfVertices, source, 0);

        return attackPathsList;
    }

    protected void explore(List<List<Vertex>> attackPathsList, int[] path, int target, boolean[] taboo, int[][] adjacencymatrix, int n, int position, int depth) throws Exception {
        path[depth] = position;
        // end
        if (position == target) {
            List<Vertex> attackPath = new ArrayList<Vertex>();
            for (int i = 0; i <= depth; i++) {
                attackPath.add(this.getVertexFromId(path[i] + 1));
            }
            attackPathsList.add(attackPath);
            return;
        }

        taboo[position] = true; // add a taboo

        // explore the remaining paths
        for (int i = 0; i < n; i++) {
            if (adjacencymatrix[position][i] == 0 || taboo[i]) continue;
            explore(attackPathsList, path, target, taboo, adjacencymatrix, n, i, depth + 1);
        }

        taboo[position] = false; // remove the taboo
    }

    /**
     * Get one path with the minimum of leaves required to arrive to this vertex
     *
     * @param v the current vertex
     * @return the minimum prerequisite leaves to reach v
     * @throws Exception
     */
    public List<Vertex> getMinimumPrerequisiteLeavesTo(Vertex v) throws Exception {
        return getMinimumPrerequisiteLeavesToRecursive(v, new boolean[this.getHighestVertexId()]);
    }

    private List<Vertex> getMinimumPrerequisiteLeavesToRecursive(Vertex v, boolean[] alreadySeen) throws Exception {
        List<Vertex> res = new ArrayList<Vertex>();
        if (v.type == VertexType.LEAF) {
            res.add(v);
            return res;
        }
        if (v.type == VertexType.OR && !alreadySeen[v.id - 1]) {
            v.computeParentsAndChildren(this);
            int minimumNumberOfLeaves = this.getNumberOfVertices() + 1;
            List<Vertex> minimumPath = new ArrayList<Vertex>();
            alreadySeen[v.id - 1] = true;
            for (int i = 0; i < v.parents.size(); i++) {
                List<Vertex> res_parent = this.getMinimumPrerequisiteLeavesToRecursive(v.parents.get(i), alreadySeen);
                if (res_parent.size() < minimumNumberOfLeaves && res_parent.size() != 0) {
                    minimumPath = res_parent;
                    minimumNumberOfLeaves = res_parent.size();
                }
            }
            alreadySeen[v.id - 1] = false;
            res.addAll(minimumPath);
            return res;
        } else if (v.type == VertexType.AND && !alreadySeen[v.id - 1]) {
            v.computeParentsAndChildren(this);
            alreadySeen[v.id - 1] = true;
            List<Vertex> toAdd = new ArrayList<Vertex>();
            for (int i = 0; i < v.parents.size(); i++) {
                List<Vertex> res_parent = this.getMinimumPrerequisiteLeavesToRecursive(v.parents.get(i), alreadySeen);
                if (res_parent.size() == 0)
                    return res;
                else {
                    toAdd.addAll(res_parent);
                }
            }
            res.addAll(toAdd);
            alreadySeen[v.id - 1] = false;
            return res;
        }
        return res;
    }

    /**
     * Delete all unreachable vertices in the attack graph
     *
     * @throws Exception
     */
    public void deleteUnreachableVertices() throws Exception {
        List<Vertex> toDelete = new ArrayList<Vertex>();
        for (int i : vertices.keySet()) {
            List<Vertex> leaves = getMinimumPrerequisiteLeavesTo(vertices.get(i));
            if (leaves.size() == 0)
                toDelete.add(vertices.get(i));
        }
        if (toDelete.size() > 0) {
            for (Vertex aToDelete : toDelete) {
                this.deleteVertex(aToDelete);
            }
            deleteUnreachableVertices();
        }
    }

    /**
     * @param v    an attack graph vertex
     * @param type a datalog fact type (ex "hacl", "vulExists", ...)
     * @return the parent vertex of v with type "type" if it exists
     */
    public Vertex getParentOfVertexWithFactCommand(Vertex v, String type) {
        List<Vertex> parents = v.parents;
        for (Vertex parent : parents) {
            if (parent.fact != null && parent.fact.type == FactType.DATALOG_FACT && parent.fact.datalogCommand != null && parent.fact.datalogCommand.command.equals(type))
                return parent;
        }
        return null;
    }

    /**
     * @return the highest vertex id number
     */
    public int getHighestVertexId() {
        int max = 0;
        for (int i : vertices.keySet()) {
            if (i >= max)
                max = i;
        }
        return max;
    }

    /**
     * @return the dom element corresponding to this attack graph XML file
     */
    public Element toDomElement() {
        Element root = new Element("attack_graph");

        //arcs
        Element arcsElement = new Element("arcs");
        root.addContent(arcsElement);
        for (Arc arc : arcs) {
            Element arcElement = new Element("arc");
            arcsElement.addContent(arcElement);
            Element srcElement = new Element("src");
            srcElement.setText(arc.destination.id + "");
            arcElement.addContent(srcElement);
            Element dstElement = new Element("dst");
            dstElement.setText(arc.source.id + "");
            arcElement.addContent(dstElement);
        }

        //vertices
        Element verticesElement = new Element("vertices");
        root.addContent(verticesElement);
        for (int key : vertices.keySet()) {
            Vertex vertex = vertices.get(key);

            Element vertexElement = new Element("vertex");
            verticesElement.addContent(vertexElement);

            Element idElement = new Element("id");
            idElement.setText(vertex.id + "");
            vertexElement.addContent(idElement);

            Element factElement = new Element("fact");
            factElement.setText(vertex.fact.factString);
            vertexElement.addContent(factElement);

            Element metricElement = new Element("metric");
            metricElement.setText(vertex.mulvalMetric + "");
            vertexElement.addContent(metricElement);

            Element typeElement = new Element("type");
            typeElement.setText(vertex.type.toString().toUpperCase());
            vertexElement.addContent(typeElement);
        }

        return root;
    }

    /**
     * Save the attack graph in an xml file
     *
     * @param filePath the path in which the attack graph is saved
     * @throws Exception
     */
    public void saveToXmlFile(String filePath) throws Exception {
        XMLOutputter output = new XMLOutputter(Format.getPrettyFormat());
        output.output(toDomElement(), new FileOutputStream(filePath));
    }

    /**
     * @return the list of possible attack paths
     * @throws Exception
     */
    public List<AttackPath> generateAttackPaths() throws Exception {
        List<AttackPath> result = new ArrayList<AttackPath>();

        List<Vertex> attackerGoals = new ArrayList<Vertex>();
        //Find attacker goals
        for (int i : vertices.keySet()) {
            Vertex vertex = vertices.get(i);
            Fact fact = vertex.fact;
            if (fact != null && fact.type == FactType.DATALOG_FACT) {
                DatalogCommand command = fact.datalogCommand;
                if (command != null && (command.command.equals("execCode") || command.command.equals("accessFile") || command.command.equals("principalCompromised"))) {
                    attackerGoals.add(vertex);
                }
            }
        }

        for (Vertex goal : attackerGoals) {
            result.addAll(this.getPossibleAttackPathsToGoTo(goal));
        }

        for (AttackPath aResult : result) {
            aResult.computeScoring();
        }
        System.out.println("Number of attack path :" + result.size());
        return result;
    }

    private List<AttackPath> getPossibleAttackPathsToGoTo(Vertex goal) throws Exception {
        return getPossibleAttackPathsToGoToRecursive(goal, new boolean[this.getHighestVertexId()]);
    }

    /**
     * @param v           the vertex
     * @param alreadySeen a boolean vector : true if the vertex as already been seen
     * @return the list of possible path attack path to access this vector
     * @throws Exception
     */
    private List<AttackPath> getPossibleAttackPathsToGoToRecursive(Vertex v, boolean[] alreadySeen) throws Exception {
        List<AttackPath> result = new ArrayList<AttackPath>();
        if (v.type == VertexType.LEAF && !alreadySeen[v.id - 1]) {
            AttackPath attackPath = new AttackPath();
            attackPath.vertices.put(v.id, v);
            result.add(attackPath);
        } else if (v.type == VertexType.OR && !alreadySeen[v.id - 1]) {
            v.computeParentsAndChildren(this);
            alreadySeen[v.id - 1] = true;
            for (int i = 0; i < v.parents.size(); i++) {
                List<AttackPath> res_parent = this.getPossibleAttackPathsToGoToRecursive(v.parents.get(i), alreadySeen);
                if (res_parent == null)
                    break;
                for (AttackPath parent : res_parent) {
                    parent.vertices.put(v.id, v);
                    parent.arcs.add(new Arc(v.parents.get(i), v));
                    result.add(parent);
                }
            }
            alreadySeen[v.id - 1] = false;
        } else if (v.type == VertexType.AND && !alreadySeen[v.id - 1]) {
            v.computeParentsAndChildren(this);
            alreadySeen[v.id - 1] = true;
            List<List<AttackPath>> result_all_parents = new ArrayList<List<AttackPath>>();
            for (int i = 0; i < v.parents.size(); i++) {
                List<AttackPath> res_parent = this.getPossibleAttackPathsToGoToRecursive(v.parents.get(i), alreadySeen);

                if (res_parent == null) {
                    alreadySeen[v.id - 1] = false;
                    return new ArrayList<AttackPath>();
                }
                for (AttackPath aRes_parent : res_parent) {
                    aRes_parent.vertices.put(v.id, v);
                    aRes_parent.arcs.add(new Arc(v.parents.get(i), v));
                }
                result_all_parents.add(res_parent);
            }
            result = computeAttackPathForAnANDVertex(result_all_parents, 0);
            alreadySeen[v.id - 1] = false;
        } else {
            return null;
        }
        return result;
    }

    /**
     * [OR] List<AttackPath>		[OR] List<AttackPath>			[OR] List<AttackPath>
     * PARENT1						PARENT2						   PARENT3
     * <p/>
     * AND LEAF : PARENT1.1 AND PARENT2.1 AND PARENT 3.1
     * OR PARENT 1.2 AND PARENT2.1 AND PARENT3.A
     * OR ....
     *
     * @param parentsAttackPathList the list of list of possible attack path of parents of a "AND" node in the attack graph :
     * @param indexInPath           the index if this list
     * @return the list of possible attack path
     */
    private List<AttackPath> computeAttackPathForAnANDVertex(List<List<AttackPath>> parentsAttackPathList, int indexInPath) {
        if (indexInPath > parentsAttackPathList.size() - 1) { //path empty
            return null;
        } else if (indexInPath == parentsAttackPathList.size() - 1) { //one parent remaining
            return parentsAttackPathList.get(indexInPath);
        } else { //more than one parent remaining
            List<AttackPath> currentAttackPathList = parentsAttackPathList.get(indexInPath);

            List<AttackPath> result_without_last_leaf = computeAttackPathForAnANDVertex(parentsAttackPathList, indexInPath + 1);

            List<AttackPath> result = new ArrayList<AttackPath>();

            for (AttackPath resultWithoutLastLeaf : result_without_last_leaf) {
                for (AttackPath aCurrentAttackPathList : currentAttackPathList) {
                    AttackPath newAttackPath = new AttackPath();
                    newAttackPath.vertices.putAll(resultWithoutLastLeaf.vertices);
                    newAttackPath.vertices.putAll(aCurrentAttackPathList.vertices);

                    newAttackPath.arcs.addAll(resultWithoutLastLeaf.arcs);
                    newAttackPath.arcs.addAll(aCurrentAttackPathList.arcs);
                    result.add(newAttackPath);
                }

            }

            return result;

        }
    }

    @Override
    public AttackGraph clone() throws CloneNotSupportedException {
        AttackGraph copie = (AttackGraph) super.clone();

        //Copie the vertices

        copie.vertices = new HashMap<Integer, Vertex>();
        for (Integer i : this.vertices.keySet()) {
            copie.vertices.put(i, this.vertices.get(i).clone());
        }

        copie.arcs = new ArrayList<Arc>();

        //Change all the vertices references in the arcs
        for (int i = 0; i < this.arcs.size(); i++) {
            Vertex destination = copie.vertices.get(this.arcs.get(i).destination.id);
            Vertex source = copie.vertices.get(this.arcs.get(i).source.id);
            Arc arc = new Arc(source, destination);
            copie.arcs.add(arc);
        }


        //change all the references in the vertices
        for (Integer i : copie.vertices.keySet()) {
            Vertex copieVertex = copie.vertices.get(i);
            copieVertex.computeParentsAndChildren(copie);
        }

        return copie;
    }

    @Override
    public String toString() {
        String result = "";

        result += "\nVertices = \n";
        for (Integer key : vertices.keySet()) {
            result += vertices.get(key) + "\n";
        }

        result += "AttackGraph : \n Arcs=\n";
        for (Arc arc : arcs) {
            result += arc + "\n";
        }

        return result;
    }

    /**
     * @param outputPath the path in which the XML attack paths are saved
     * @return The list of attack path extracted from this attack graph
     * @throws Exception
     */
    public List<AttackPath> scoreAttackGraphAndGetAttackPaths(String outputPath, double previousMaxScore) throws Exception {
        double[] vertexIDTable = new double[this.getNumberOfVertices()];
        String[] vertexFactTable = new String[this.getNumberOfVertices()];
        double[] vertexMulvalMetricTable = new double[this.getNumberOfVertices()];
        String[] vertexTypeTable = new String[this.getNumberOfVertices()];

        double[] arcSrcTable = new double[this.arcs.size()];
        double[] arcDstTable = new double[this.arcs.size()];
        ImpactMetric[][] impactMetrics = new ImpactMetric[this.getNumberOfVertices()][];

        int i = 0;
        System.out.println("Generate input for scoring function");
        for (Integer key : this.vertices.keySet()) {
            Vertex vertex = this.vertices.get(key);

            vertexIDTable[i] = vertex.id;
            vertexFactTable[i] = vertex.fact.factString;
            vertexMulvalMetricTable[i] = vertex.mulvalMetric;
            vertexTypeTable[i] = vertex.type.toString().toUpperCase();
            impactMetrics[i] = new ImpactMetric[vertex.impactMetrics.size()];
            for(int j = 0; j < vertex.impactMetrics.size() ; j++) {
                impactMetrics[i][j] = vertex.impactMetrics.get(j);
            }

            i++;
        }

        for (int j = 0; j < this.arcs.size(); j++) {
            Arc arc = this.arcs.get(j);
            arcDstTable[j] = arc.source.id;
            arcSrcTable[j] = arc.destination.id;
        }
        System.out.println("Compute global score and compute attack paths");
        this.globalScore = Launch.main(vertexIDTable, vertexFactTable, vertexMulvalMetricTable, vertexTypeTable,
                arcSrcTable, arcDstTable, impactMetrics, outputPath, previousMaxScore);

        return AttackPath.loadAttackPathsFromFile(outputPath, this);
    }

    /**
     * Browse all vertices and check if the vertex can have a metric (is an execCode)
     *
     * @param informationSystem the information system
     * @throws Exception
     */
    public void loadMetricsFromTopology(InformationSystem informationSystem) throws Exception {
        for (Integer key : vertices.keySet()) {
            Vertex vertex = vertices.get(key);
            if (vertex.fact != null && vertex.fact.type == FactType.DATALOG_FACT && vertex.fact.datalogCommand.command.equals("execCode")) {
                //We are in an execCode
                String hostName = vertex.fact.datalogCommand.params[0];
                if (hostName != null && !hostName.isEmpty()) {
                    InformationSystemHost host = informationSystem.getHostByNameOrIPAddress(hostName);
                    if (host != null) {
                        ImpactMetric metric = new ImpactMetric(host.getMetric(), 1);
                        vertex.impactMetrics.add(metric);
                    }
                }

            }
        }
    }

    /**
     * @param is the information system
     * @return the security requirements impacted by this attack path
     * @throws Exception
     */
    public List<SecurityRequirement> computeRelatedImactedSecurityRequirements(InformationSystem is) throws Exception {
        List<SecurityRequirement> impactedRequirements = new ArrayList<SecurityRequirement>();

        for (int i : this.vertices.keySet()) {
            Vertex vertex = this.vertices.get(i);
            if (vertex.fact != null && vertex.fact.type == FactType.DATALOG_FACT) {
                DatalogCommand command = vertex.fact.datalogCommand;
                if (command.command.equals("execCode")) {
                    String machineName = command.params[0];
                    InformationSystemHost machine = is.existingMachineByNameOrIPAddress(machineName);
                    if (machine != null) {
                        for (int j = 0; j < machine.getSecurityRequirements().size(); j++) {
                            SecurityRequirement secReq = machine.getSecurityRequirements().get(j);
                            if (!impactedRequirements.contains(secReq)) {
                                impactedRequirements.add(secReq);
                            }
                        }
                    }
                }

            }
        }

        return impactedRequirements;

    }

    /**
     * @param informationSystem the information system
     * @return The topology Graph associated to this attack path
     * @throws Exception
     */
    public InformationSystemGraph getRelatedTopologyGraph(InformationSystem informationSystem) throws Exception {
        InformationSystemGraph result = new InformationSystemGraph();

        List<Vertex> vertices = new ArrayList<Vertex>(this.vertices.values());
        for (Vertex vertex : vertices) {
            if (vertex.fact.type == FactType.DATALOG_FACT && vertex.fact.datalogCommand != null) {
                DatalogCommand command = vertex.fact.datalogCommand;
                switch (command.command) {
                    case "hacl":
                        InformationSystemGraphVertex from = null;
                        InformationSystemGraphVertex to = null;
                        String relatedVulneravility = null;
                        if (command.params[0].equals("internet") || command.params[0].equals("1.1.1.1") || command.params[0].equals("internet_host")) {
                            from = result.getMachineVertex(informationSystem.getHostByNameOrIPAddress("internet_host"));
                        } else {
                            InformationSystemHost machine = informationSystem.getHostByNameOrIPAddress(command.params[0]);
                            if (machine != null) {
                                from = result.getMachineVertex(machine);
                            }
                        }
                        if (command.params[1].equals("internet") || command.params[1].equals("1.1.1.1"))
                            to = result.getMachineVertex(informationSystem.getHostByNameOrIPAddress("1.1.1.1"));
                        else {
                            InformationSystemHost machine = informationSystem.getHostByNameOrIPAddress(command.params[1]);
                            if (machine != null) {
                                to = result.getMachineVertex(machine);
                            }
                        }
                        if (from != null && to != null) {
                            InformationSystemGraphArc arc = new InformationSystemGraphArc();
                            arc.setSource(from);
                            arc.setDestination(to);
                            vertex.computeParentsAndChildren(this);
                            //Try to find (if applicable) the related vulnerability
                            Vertex directAccessChild = vertex.childOfType(true, "direct network access");
                            if (directAccessChild == null) {
                                directAccessChild = vertex.childOfType(true, "multi-hop access");
                            }
                            if (directAccessChild != null) {
                                directAccessChild.computeParentsAndChildren(this);
                                Vertex netAccessChild = directAccessChild.childOfType(false, "netAccess");
                                if (netAccessChild != null) {
                                    netAccessChild.computeParentsAndChildren(this);
                                    Vertex remoteExploitChild = netAccessChild.childOfType(true, "remote exploit of a server program");
                                    if (remoteExploitChild != null) {
                                        remoteExploitChild.computeParentsAndChildren(this);
                                        Vertex vulnExistParent = remoteExploitChild.parentOfType(false, "vulExists");
                                        if (vulnExistParent != null && vulnExistParent.fact.datalogCommand.params.length > 2) {
                                            relatedVulneravility = vulnExistParent.fact.datalogCommand.params[1];
                                        }
                                    }
                                }

                            }

                            if (relatedVulneravility != null)
                                arc.setRelatedVulnerability(relatedVulneravility);

                            if (!result.getArcs().contains(arc))
                                result.getArcs().add(arc);
                        }
                        break;
                    case "attackerLocated":
                        InformationSystemGraphVertex attackerVertex = null;
                        if (command.params[0].equals("internet") || command.params[0].equals("1.1.1.1"))
                            attackerVertex = result.getMachineVertex(informationSystem.getHostByNameOrIPAddress("1.1.1.1"));
                        else {
                            InformationSystemHost machine = informationSystem.getHostByNameOrIPAddress(command.params[0]);
                            if (machine != null) {
                                attackerVertex = result.getMachineVertex(machine);
                            }
                        }
                        if (attackerVertex != null)
                            attackerVertex.setMachineOfAttacker(true);
                        break;
                    case "vulExists":
                        InformationSystemHost machine = informationSystem.getHostByNameOrIPAddress(command.params[0]);
                        if (machine != null) {
                            result.getMachineVertex(machine).setCompromised(true);
                        }
                        break;
                }
            }
        }

        return result;
    }
} public class AttackPath extends MulvalAttackGraph implements Cloneable {

    /**
     * The scoring of the attack path (should be between 0 and 1)
     */
    public double scoring = 0;
    /**
     * The goal of the attacker
     */
    Vertex goal = null;

    /**
     * @param leavesToCorrect      the list of leaves that should be corrected
     * @param indexInPath          the index where we are in the list of leaves
     * @param howToRemediateLeaves a vector of how each leaf can be corrected
     * @return The list of remediation actions : A1 OR A2 OR A3 where A1 = A1.1 AND A1.2 AND A1.3 ...
     */
    static private List<List<RemediationAction>> computeRemediationToPathWithLeafRemediations(List<Vertex> leavesToCorrect, int indexInPath, HashMap<Integer, List<List<RemediationAction>>> howToRemediateLeaves) {

        //Recursive function on the number of remaining leaves to correct

        if (indexInPath > leavesToCorrect.size() - 1) { //path empty
            //Normally this case should never happen

            return new ArrayList<List<RemediationAction>>();
        } else if (indexInPath == leavesToCorrect.size() - 1) { //one leaf
            //Stop case of the recursion, we return the mean to correct only one leaf
            Vertex currentLeaf = leavesToCorrect.get(indexInPath);

            return howToRemediateLeaves.get(currentLeaf.id);
        } else { //more than one leaf in path
            Vertex currentLeaf = leavesToCorrect.get(indexInPath);

            //First we correct the other leaves and get the result
            List<List<RemediationAction>> result_remediation_path_without_last_leaf = computeRemediationToPathWithLeafRemediations(leavesToCorrect, indexInPath + 1, howToRemediateLeaves);

            List<List<RemediationAction>> resultWithoutLastLeaf = new ArrayList<List<RemediationAction>>();
            for (List<RemediationAction> aResult_remediation_path_without_last_leaf1 : result_remediation_path_without_last_leaf) {
                List<RemediationAction> contentResultToDuplicate = new ArrayList<RemediationAction>(aResult_remediation_path_without_last_leaf1);
                resultWithoutLastLeaf.add(contentResultToDuplicate);
            }

            List<List<RemediationAction>> result_to_duplicate = new ArrayList<List<RemediationAction>>();
            for (List<RemediationAction> aResult_remediation_path_without_last_leaf : result_remediation_path_without_last_leaf) {
                List<RemediationAction> contentResultToDuplicate = new ArrayList<RemediationAction>(aResult_remediation_path_without_last_leaf);
                result_to_duplicate.add(contentResultToDuplicate);
            }


            List<List<RemediationAction>> result = new ArrayList<List<RemediationAction>>();

            //Then, we add each element of remediation concerning the current leaf:
            List<List<RemediationAction>> howToRemediateLeaf = howToRemediateLeaves.get(currentLeaf.id);

            //FOR ALL OR CONDITIONS (remediations of the current leaf)
            //we duplicate the old result (result_remediation_path_without_last_leaf) howToRemediateLeaf.size() - 1 times
            //and add all the howToRemediateLeaf.get(i) to each duplicates

            //For the first OR, no duplicate is useful
            //Add the howToRemediateLeaf.get(i) to all OR elements
            for (List<RemediationAction> aResultWithoutLastLeaf : resultWithoutLastLeaf) {
                aResultWithoutLastLeaf.addAll(new ArrayList<RemediationAction>(howToRemediateLeaf.get(0)));
            }
            result.addAll(resultWithoutLastLeaf);

            //For the other OR, we can duplicate
            for (int i = 1; i < howToRemediateLeaf.size(); i++) {
                //Create the duplicate and add the howToRemediateLeaf.get(i) results into it
                List<List<RemediationAction>> duplicate = new ArrayList<List<RemediationAction>>();
                for (List<RemediationAction> aResult_to_duplicate : result_to_duplicate) {
                    List<RemediationAction> duplicateContent = new ArrayList<RemediationAction>(aResult_to_duplicate);
                    duplicate.add(duplicateContent);
                }

                //For all OR of the duplicate, add the howToRemediateLeaf.get(i)
                for (List<RemediationAction> aDuplicate : duplicate) {
                    aDuplicate.addAll(new ArrayList<RemediationAction>(howToRemediateLeaf.get(i)));
                }
                result.addAll(duplicate);
            }

            return result;
        }
    }

    /**
     * Compute all possible combination of k integers < to n
     *
     * @param k integer < n
     * @param n integer
     * @return the list of list of k integers
     */
    public static List<List<Integer>> combination(int k, int n) {
        List<Integer> listNumber = new ArrayList<Integer>();
        for (int i = 0; i < n; i++) {
            listNumber.add(i);
        }
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        combinationRecursive(k, listNumber, 0, new ArrayList<Integer>(), result);
        return result;
    }

    /**
     * Main recursive function used to calculate the combination list combination(k,n)
     */
    private static void combinationRecursive(int k, List<Integer> listNumber, int startListNumber, List<Integer> temporaryResult, List<List<Integer>> result) {
        if (k == 0) {
            result.add(new ArrayList<Integer>(temporaryResult));
            temporaryResult.clear();
            return;
        }
        if (listNumber.size() == startListNumber) return;

        List<Integer> temporaryResult2 = new ArrayList<Integer>(temporaryResult);
        temporaryResult.add(listNumber.get(startListNumber));
        combinationRecursive(k - 1, listNumber, startListNumber + 1, temporaryResult, result);
        combinationRecursive(k, listNumber, startListNumber + 1, temporaryResult2, result);
    }

    /**
     * Get all the machines that are involved in this attack path considered only as a list of vertices
     *
     * @param topology   the Network topology
     * @param attackPath the attack path to explore
     * @return the list of machine involved in this attack path
     * @throws Exception
     */
    public static List<InformationSystemHost> getInvolvedMachines(InformationSystem topology, List<Vertex> attackPath) throws Exception {
        List<InformationSystemHost> result = new ArrayList<InformationSystemHost>();
        for (Vertex vertex : attackPath) {
            if (vertex.fact != null && vertex.fact.type != FactType.RULE) {
                InformationSystemHost machine = vertex.getRelatedMachine(topology);
                result.add(machine);
            }
        }
        return result;
    }

    public static List<AttackPath> loadAttackPathsFromFile(String attackPathsFilePath, AttackGraph relatedAttackGraph) throws Exception {
        FileInputStream file = new FileInputStream(attackPathsFilePath);
        SAXBuilder sxb = new SAXBuilder();
        Document document = sxb.build(file);
        Element root = document.getRootElement();

        List<AttackPath> result = new ArrayList<AttackPath>();

        List<Element> attackPathsElements = root.getChildren("attack_path");
        if (!attackPathsElements.isEmpty()) {
            for (Element attackPathElement : attackPathsElements) {
                if (attackPathElement != null) {
                    AttackPath attackPath = new AttackPath();
                    attackPath.loadFromDomElementAndAttackGraph(attackPathElement, relatedAttackGraph);
                    result.add(attackPath);
                }
            }
        }
        sortAttackPaths(result);

        return result;

    }

    /**
     * Sort attack paths with their scoring in descending order
     */
    public static void sortAttackPaths(List<AttackPath> attackPathList) {
        Collections.sort(attackPathList, new AttackPath.AttackPathComparator());
    }

    /**
     * @return the leaves of the attack graph
     */
    public List<Vertex> getLeavesThatCanBeRemediated() {
        List<Vertex> result = new ArrayList<Vertex>();
        for (int i : this.vertices.keySet()) {
            Vertex vertex = this.vertices.get(i);
            vertex.computeParentsAndChildren(this);
            if (vertex.parents.size() == 0) {
                if (vertex.fact != null && vertex.fact.datalogCommand != null
                        && vertex.fact.datalogCommand.command != null) {
                    String command = vertex.fact.datalogCommand.command;
                    if (command.equals("vulExists") || command.equals("hacl") || command.equals("haclprimit") || command.toLowerCase().contains("vlan") || command.contains("attackerLocated"))
                        result.add(vertex);
                }
            } else {
                if (vertex.fact != null && vertex.fact.datalogCommand != null
                        && vertex.fact.datalogCommand.command != null && vertex.fact.datalogCommand.command.equals("hacl")) {
                    result.add(vertex);
                }
            }
        }
        return result;
    }

    /**
     * @return the goal of the attack graph
     */
    public Vertex getGoal() {
        if (goal == null) {
            for (int i : this.vertices.keySet()) {
                Vertex vertex = this.vertices.get(i);
                vertex.computeParentsAndChildren(this);
                if (vertex.children.size() == 0)
                    goal = vertex;
            }
        }
        return goal;
    }

    /**
     * @param topology the network topology
     * @param conn     database connection
     * @return the list of possible remediation actions to remediate this attack path : remediation[1] OR remediation[2] OR remediation[3] ; remediation[1] = remediation[1][1] AND remediation[1][2]...
     */
    public List<List<RemediationAction>> getRemediationAction(InformationSystem topology, Connection conn, String costParametersFolder) throws Exception {
        List<List<RemediationAction>> result = new ArrayList<List<RemediationAction>>();


        List<Vertex> leaves = this.getLeavesThatCanBeRemediated();

        //Compute all possible sufficient combination of leaves to cut the attack path
        List<List<Vertex>> sufficientLeavesToCutPath = new ArrayList<List<Vertex>>();
        List<Vertex> remainingLeaves = new ArrayList<Vertex>(leaves);
        int simultaneousLeavesNumber = 1;

        while (simultaneousLeavesNumber <= remainingLeaves.size()) { //While we take a number of leaves lower than the number of remaining leaves
            List<List<Integer>> combination = combination(simultaneousLeavesNumber, remainingLeaves.size());
            List<Vertex> leavesSufficient = new ArrayList<Vertex>();

            for (List<Integer> aCombination : combination) {
                List<Vertex> leavesToTest = new ArrayList<Vertex>();
                for (Integer anACombination : aCombination) {
                    leavesToTest.add(remainingLeaves.get(anACombination));
                }
                if (leavesMandatoryForGoal(leavesToTest)) {
                    sufficientLeavesToCutPath.add(leavesToTest);
                    leavesSufficient.addAll(leavesToTest);
                }
            }

            for (Vertex aLeavesSufficient : leavesSufficient) {
                remainingLeaves.remove(aLeavesSufficient);
            }

            simultaneousLeavesNumber++;
        }

        //Create a hashlist of the list of remediation for each leaf (possible_actions[1] OR possible_actions[2] OR possible_actions[3] .... with possible_actions[1] = possible_actions[1][1] AND possible_actions[1][2] AND possible_actions[1][3]
        HashMap<Integer, List<List<RemediationAction>>> howToRemediateLeaves = new HashMap<Integer, List<List<RemediationAction>>>();
        for (Vertex leaf : leaves) {
            howToRemediateLeaves.put(leaf.id, getRemediationActionForLeaf(leaf, topology, conn, costParametersFolder));
        }


        //Try to see how to remediate each list of leaf
        for (List<Vertex> aSufficientLeavesToCutPath : sufficientLeavesToCutPath) {
            //List<RemediationAction> pathRemediations = new ArrayList<RemediationAction>();
            boolean pathCanBeRemediated = true;

            //For all leaf list that can cut the attack path
            for (Vertex leaf : aSufficientLeavesToCutPath) {
                if (howToRemediateLeaves.get(leaf.id).isEmpty())
                    pathCanBeRemediated = false;

            }
            if (pathCanBeRemediated) {
                result.addAll(computeRemediationToPathWithLeafRemediations(aSufficientLeavesToCutPath, 0, howToRemediateLeaves));
            }
        }

        return result;
    }

    /**
     * @param topology the network topology
     * @param conn     database connection
     * @return the list of possible remediation actions to remediate this attack path : remediation[1] OR remediation[2] OR remediation[3] ; remediation[1] = remediation[1][1] AND remediation[1][2]... [Withour snort rules]
     */
    public List<List<RemediationAction>> getRemedationActions(InformationSystem topology, Connection conn, String costParametersFolder) throws Exception {
        List<List<RemediationAction>> result = new ArrayList<List<RemediationAction>>();


        List<Vertex> leaves = this.getLeavesThatCanBeRemediated();

        //Compute all possible sufficient combination of leaves to cut the attack path
        List<List<Vertex>> sufficientLeavesToCutPath = new ArrayList<List<Vertex>>();
        List<Vertex> remainingLeaves = new ArrayList<Vertex>(leaves);
        int simultaneousLeavesNumber = 1;

        while (simultaneousLeavesNumber <= remainingLeaves.size()) { //While we take a number of leaves lower than the number of remaining leaves
            List<List<Integer>> combination = combination(simultaneousLeavesNumber, remainingLeaves.size());
            List<Vertex> leavesSufficient = new ArrayList<Vertex>();

            //TODO : improve this function : can be much faster.
            for (List<Integer> aCombination : combination) {
                List<Vertex> leavesToTest = new ArrayList<Vertex>();
                for (Integer anACombination : aCombination) {
                    leavesToTest.add(remainingLeaves.get(anACombination));
                }
                if (leavesMandatoryForGoal(leavesToTest)) {
                    sufficientLeavesToCutPath.add(leavesToTest);
                    leavesSufficient.addAll(leavesToTest);
                }
            }

            for (Vertex aLeavesSufficient : leavesSufficient) {
                remainingLeaves.remove(aLeavesSufficient);
            }

            simultaneousLeavesNumber++;
        }

        //Create a hashlist of the list of remediation for each leaf (possible_actions[1] OR possible_actions[2] OR possible_actions[3] .... with possible_actions[1] = possible_actions[1][1] AND possible_actions[1][2] AND possible_actions[1][3]
        HashMap<Integer, List<List<RemediationAction>>> howToRemediateLeaves = new HashMap<Integer, List<List<RemediationAction>>>();
        for (Vertex leaf : leaves) {
            howToRemediateLeaves.put(leaf.id, getRemediationActionForLeaf(leaf, topology, conn, costParametersFolder, true));
        }


        //Try to see how to remediate each list of leaf
        for (List<Vertex> aSufficientLeavesToCutPath : sufficientLeavesToCutPath) {
            //List<RemediationAction> pathRemediations = new ArrayList<RemediationAction>();
            boolean pathCanBeRemediated = true;

            //For all leaf list that can cut the attack path
            for (Vertex leaf : aSufficientLeavesToCutPath) {
                if (howToRemediateLeaves.get(leaf.id).isEmpty())
                    pathCanBeRemediated = false;

            }
            if (pathCanBeRemediated) {
                result.addAll(computeRemediationToPathWithLeafRemediations(aSufficientLeavesToCutPath, 0, howToRemediateLeaves));
            }
        }

        return result;
    }

    /**
     * @param topology             the network topology
     * @param conn                 the database connection
     * @param costParametersFolder the folder where the cost parameters are stored
     * @return the list of deployable remediations without snort rules
     * @throws Exception
     */
    public List<DeployableRemediation> getDeployableRemediations(InformationSystem topology, Connection conn, String costParametersFolder) throws Exception {
        List<List<RemediationAction>> remediationActions = this.getRemedationActions(topology, conn, costParametersFolder);
        List<DeployableRemediation> result = new ArrayList<DeployableRemediation>();

        //For all "OR" remediations
        for (List<RemediationAction> remediationAction1 : remediationActions) {
            List<DeployableRemediation> result_tmp = new ArrayList<DeployableRemediation>(); //Result for only this group of remediations
            DeployableRemediation dr = new DeployableRemediation(this, topology);
            dr.setActions(new ArrayList<DeployableRemediationAction>());
            result_tmp.add(dr);

            //For all "AND" remediations
            for (RemediationAction remediationAction : remediationAction1) {
                if (remediationAction.getPossibleMachines().size() > 1) {
                    //ALL MACHINES EXCEPT THE FIRST ONE
                    List<DeployableRemediation> resultForThisRemediationAction = new ArrayList<DeployableRemediation>(); //Result for only this group of remediations
                    for (int m = 1; m < remediationAction.getPossibleMachines().size(); m++) {
                        //For all the previously added Deployable Remediation Actions
                        for (DeployableRemediation aResult_tmp : result_tmp) {
                            DeployableRemediation newdr = new DeployableRemediation(this, topology);
                            resultForThisRemediationAction.add(newdr);
                            newdr.getActions().addAll(aResult_tmp.getActions());
                            DeployableRemediationAction deployableRemediationAction = new DeployableRemediationAction();
                            deployableRemediationAction.setRemediationAction(remediationAction);
                            deployableRemediationAction.setHost(remediationAction.getPossibleMachines().get(m));
                            newdr.getActions().add(deployableRemediationAction);
                        }
                    }
                    //FIRST MACHINE
                    for (DeployableRemediation aResult_tmp : result_tmp) { //For all the previously added Deployable Remediation Actions
                        DeployableRemediationAction deployableRemediationAction = new DeployableRemediationAction();
                        deployableRemediationAction.setRemediationAction(remediationAction);
                        deployableRemediationAction.setHost(remediationAction.getPossibleMachines().get(0));
                        aResult_tmp.getActions().add(deployableRemediationAction);
                    }
                    result_tmp.addAll(resultForThisRemediationAction);
                } else if (remediationAction.getPossibleMachines().size() == 1) {
                    for (DeployableRemediation aResult_tmp : result_tmp) {
                        DeployableRemediationAction deployableRemediationAction = new DeployableRemediationAction();
                        deployableRemediationAction.setRemediationAction(remediationAction);
                        deployableRemediationAction.setHost(remediationAction.getPossibleMachines().get(0));
                        aResult_tmp.getActions().add(deployableRemediationAction);
                    }
                }
            }

            //Add all the non empty Deployable Remediation Action to the result
            for (DeployableRemediation aResult_tmp : result_tmp) {
                if (aResult_tmp.getActions().size() > 0)
                    result.add(aResult_tmp);
            }
        }

        //Compute the cost of all remediation action
        for (DeployableRemediation aResult : result) {
            aResult.computeCost();
        }

        Collections.sort(result, new DeployableRemediationComparator());

        return result;
    }

    /**
     * This function compute the scoring of this attack path (float between 0 and 1 : 1 = will arrive ; 0 = can't arrive
     */
    public void computeScoring() {
        scoring = 1.;
        for (int i : this.vertices.keySet()) {
            Vertex vertex = this.vertices.get(i);
            if (vertex.fact != null && vertex.fact.type == FactType.DATALOG_FACT) {
                DatalogCommand command = vertex.fact.datalogCommand;
                if (command.command.equals("vulExists")) {
                    scoring *= 1. / 2;
                }
                if (command.command.equals("cvss") && command.params[1].equals("l")) {
                    scoring *= 1. / 5;
                }
                if (command.command.equals("cvss") && command.params[1].equals("m")) {
                    scoring *= 1. / 10;
                }
                if (command.command.equals("cvss") && command.params[1].equals("h")) {
                    scoring *= 1. / 20;
                } else if (command.command.equals("inCompetent")) {
                    scoring *= 1. / 100;
                }
                //Cheat to have the good attack path for demo first :
                if (command.command.equals("vulExists") && command.params[1].equals("CVE-2004-1315")) {
                    scoring *= 10;
                }
                if (command.command.equals("vulExists") && command.params[1].equals("CVE-2012-3951")) {
                    scoring *= 10;
                }
                if (command.command.equals("networkServiceInfo") && command.params[1].equals("sonicwall_scrutinizer")) {
                    scoring *= 10;
                }
                if (command.command.equals("hacl") && command.params[0].equals("192.168.240.200") && command.params[1].equals("192.168.240.100") && command.params[3].equals("3306")) {
                    scoring *= 10;
                }
                if (command.command.equals("hacl") && command.params[0].equals("internet") && command.params[1].equals("192.168.240.200") && command.params[3].equals("80")) {
                    scoring *= 10;
                }
                if (command.command.equals("execCode") && command.params[0].equals("192.168.240.100")) {
                    scoring *= 10;
                }
                if (command.command.equals("execCode") && command.params[0].equals("192.168.240.200")) {
                    scoring *= 10;
                }
                if (command.command.equals("netAccess") && command.params[0].equals("192.168.240.200")) {
                    scoring *= 10;
                }
            }
        }
    }

    /**
     * Print the list of remediations that could be applied to prevent the attack
     *
     * @param topology the network topology
     * @param conn     the database connection
     * @throws Exception
     */
    public void printListRemediations(InformationSystem topology, Connection conn) throws Exception {
        List<Vertex> leaves = this.getLeavesThatCanBeRemediated();
        if (this.getGoal() != null) {
            System.out.print("The goal of this attack is vertex " + this.getGoal().id);
            if (this.getGoal().fact != null && this.getGoal().fact.factString != null)
                System.out.print(" : " + this.getGoal().fact.factString);
            System.out.println();
        }
        System.out.println("The attack path contains " + leaves.size() + " leaves : ");

        List<List<Vertex>> sufficientLeavesToCutPath = new ArrayList<List<Vertex>>();
        List<Vertex> remainingLeaves = new ArrayList<Vertex>(leaves);
        int simultaneousLeavesNumber = 1;

        while (simultaneousLeavesNumber <= remainingLeaves.size()) { //While we take a number of leaves lower than the number of remaining leaves
            List<List<Integer>> combination = combination(simultaneousLeavesNumber, remainingLeaves.size());
            List<Vertex> leavesSufficient = new ArrayList<Vertex>();

            for (List<Integer> aCombination : combination) {
                List<Vertex> leavesToTest = new ArrayList<Vertex>();
                for (Integer anACombination : aCombination) {
                    leavesToTest.add(remainingLeaves.get(anACombination));
                }
                if (leavesMandatoryForGoal(leavesToTest)) {
                    sufficientLeavesToCutPath.add(leavesToTest);
                    leavesSufficient.addAll(leavesToTest);
                }
            }

            for (Vertex aLeavesSufficient : leavesSufficient) {
                remainingLeaves.remove(aLeavesSufficient);
            }

            simultaneousLeavesNumber++;
        }

        System.out.println("Here are the list of all the combinations of leaves that permit to cut the attack path ");
        for (int i = 0; i < sufficientLeavesToCutPath.size(); i++) {
            System.out.print("Combination  " + (i + 1) + " : ");
            for (int j = 0; j < sufficientLeavesToCutPath.get(i).size(); j++) {
                System.out.print("Leaf " + sufficientLeavesToCutPath.get(i).get(j).id);
                if (j < sufficientLeavesToCutPath.get(i).size() - 1)
                    System.out.print(" + ");
            }
            System.out.println();
        }

        System.out.println("\n------------------------------------");
        System.out.println("Here is how to remediate each leaf :");
        System.out.println("------------------------------------");

        for (Vertex leaf : leaves) {
            System.out.print(remediateLeaf(leaf, topology, conn));
            System.out.println("----------------------------------");
        }

    }

    /**
     * @param leaf     An attack path leaf
     * @param topology the network topology
     * @param conn     the database connection
     * @return The string that contains the text to remediate a leaf
     * @throws Exception
     */
    public String remediateLeaf(Vertex leaf, InformationSystem topology, Connection conn) throws Exception {
        String result = "";
        result += "* Leaf " + leaf.id + "\n";
        if (leaf.fact != null && leaf.fact.type == FactType.DATALOG_FACT && leaf.fact.datalogCommand != null) {
            DatalogCommand command = leaf.fact.datalogCommand;
            result += "Datalog fact : " + command.command + "\n";

            switch (command.command) {
                case "vulExists": {
                    Vulnerability vuln = new Vulnerability(conn, Vulnerability.getIdVulnerabilityFromCVE(command.params[1], conn));
                    List<List<InformationSystemHost>> attackerPath = getAttackerRouteToAVulnerability(leaf, topology);
                    result += "To exploit the vulnerability " + vuln.cve + " the packets of the attacker will pass the following machines : " + "\n";
                    for (int j = 0; j < attackerPath.size(); j++) {
                        if (attackerPath.size() > 1)
                            result += "Path " + (j + 1) + " : " + "\n";
                        result += "- ";
                        for (int k = 0; k < attackerPath.get(j).size(); k++) {
                            result += attackerPath.get(j).get(k).getName();
                            if (k < attackerPath.get(j).size() - 1)
                                result += " -> ";
                        }
                        result += "\n";
                        List<Patch> patches = vuln.getPatchs(conn);
                        List<Rule> rules = vuln.getRules(conn);
                        if (patches.size() > 0) {
                            result += "To protect against this vulnerability, the following patch(es) can be applied on machine \"" + leaf.getRelatedMachine(topology).getName() + "\" : " + "\n";
                            for (int m = 0; m < patches.size(); m++) {
                                result += patches.get(m).getLink();
                                if (m < patches.size() - 1)
                                    result += " ; ";
                            }
                            result += "\n";
                        }
                        if (rules.size() > 0) {
                            result += "To protect against this vulnerability, the following rule(s) can be used on at least one machine of the attacker path : " + "\n";
                            for (Rule rule : rules) {
                                result += "-" + rule.getRule() + "\n";
                            }
                        }
                    }
                    break;
                }
                case "inCompetent":
                    result += "To protect against this attack, the user \"" + command.params[0] + "\" should be trained " + "\n";
                    break;
                case "attackerLocated":
                    result += "To protect against this attack, people should know that the attacker is located on \"" + command.params[0] + "\" \n";
                    break;
                case "hasAccount":
                    result += "To protect against this attack, the account \"" + command.params[2] + "\" on the machine \"" + leaf.getRelatedMachine(topology).getName() + "\" should be closed\n";
                    break;
                case "hacl": {
                    InformationSystemHost from = topology.getHostByNameOrIPAddress(command.params[0]);
                    InformationSystemHost to = topology.getHostByNameOrIPAddress(command.params[1]);
                    List<List<InformationSystemHost>> attackerPath = command.getRoutesBetweenHostsOfHacl(topology);
                    result += "The attacker packets will use one of the following paths : \n";
                    for (int j = 0; j < attackerPath.size(); j++) {
                        if (attackerPath.size() > 1)
                            result += "Path " + (j + 1) + " : " + "\n";
                        result += "- ";
                        for (int k = 0; k < attackerPath.get(j).size(); k++) {
                            result += attackerPath.get(j).get(k).getName();
                            if (k < attackerPath.get(j).size() - 1)
                                result += " -> ";
                        }
                        result += "\n";
                    }
                    System.out.println("The following firewall rule should be deployed on all the paths between \"" + from.getName() + "\" and \"" + to.getName() + "\". (See above)");
                    if (command.params[0].equals("internet")) {
                        result += "DROP\t" + Protocol.getProtocolFromString(command.params[2]) + "\t--\t0.0.0.0/0\t" + to.getFirstIPAddress().getAddress() + "/32\t" + "dpt:" + Service.portStringToInt(command.params[3]) + "\n";
                    } else if (command.params[1].equals("internet")) {
                        result += "DROP\t" + Protocol.getProtocolFromString(command.params[2]) + "\t--\t" + from.getFirstIPAddress().getAddress() + "/32\t0.0.0.0/0\t" + "dpt:" + Service.portStringToInt(command.params[3]) + "\n";
                    } else {
                        result += "DROP\t" + Protocol.getProtocolFromString(command.params[2]) + "\t--\t" + from.getFirstIPAddress().getAddress() + "/32\t" + to.getFirstIPAddress().getAddress() + "/32\t" + "dpt:" + Service.portStringToInt(command.params[3]) + "\n";
                    }
                    break;
                }
            }
        } else {
            throw new Exception("Leaf is not a datalog fact");
        }
        if (leafMandatoryForGoal(leaf))
            result += "If this can be corrected, this attack path will be broken" + "\n";
        else
            result += "Deleting only this vulnerability is not sufficient to cut the attack path" + "\n";
        return result;
    }

    /**
     * @param leaf     An attack path leaf
     * @param topology the network topology
     * @param conn     the database connection
     * @return the possible remediation action to remediate this leaf. To remediate the leaf, we can apply remediation[1] OR remadiation[2] OR remediation[3]
     * the remediation[1] is remediation[1][1] AND remediation[1][2] AND remediation[1][3] etc...
     * @throws Exception
     */
    public List<List<RemediationAction>> getRemediationActionForLeaf(Vertex leaf, InformationSystem topology, Connection conn, String costParametersFolder) throws Exception {
        return getRemediationActionForLeaf(leaf, topology, conn, costParametersFolder, true);
    }

    /**
     * @param leaf         An attack path leaf
     * @param topology     the network topology
     * @param conn         the database connection
     * @param useSnortRule : if true, use the snort rules else don't use it for remediation
     * @return the possible remediation action to remediate this leaf. To remediate the leaf, we can apply remediation[1] OR remadiation[2] OR remediation[3]
     * the remediation[1] is remediation[1][1] AND remediation[1][2] AND remediation[1][3] etc...
     * @throws Exception
     */
    public List<List<RemediationAction>> getRemediationActionForLeaf(Vertex leaf, InformationSystem topology, Connection conn, String costParametersFolder, boolean useSnortRule) throws Exception {
        List<List<RemediationAction>> result = new ArrayList<List<RemediationAction>>();
        if (leaf.fact != null && leaf.fact.type == FactType.DATALOG_FACT && leaf.fact.datalogCommand != null) {
            DatalogCommand command = leaf.fact.datalogCommand;

            switch (command.command) {
                case "vulExists": {
                    List<RemediationAction> remediateVulnerability = new ArrayList<RemediationAction>();
                    Vulnerability vulnerability = new Vulnerability(conn, Vulnerability.getIdVulnerabilityFromCVE(command.params[1], conn));
                    List<List<InformationSystemHost>> attackerPath = getAttackerRouteToAVulnerability(leaf, topology);

                    List<Patch> patches = vulnerability.getPatchs(conn); //Get the path of this vulnerability

                    List<Rule> rules = vulnerability.getRules(conn); //Get the snort rules related to this vulnerability

                    if (patches.size() > 0) {
                        RemediationAction remediation = new RemediationAction(ActionType.APPLY_PATCH, costParametersFolder);
                        remediation.setRelatedVertex(leaf);
                        remediation.getPossibleMachines().add(leaf.getRelatedMachine(topology));

                        for (Patch patche : patches) {
                            remediation.getRemediationParameters().add(patche);
                        }
                        remediateVulnerability.add(remediation);
                        result.add(remediateVulnerability);
                    }
                    if (rules.size() > 0 && useSnortRule) {
                        //If there is a rule, to remediate the leaf, we can deploy rule 1 OR rule 2 OR rule 3 but on ALL possible paths
                        List<RemediationAction> detectVulnerabilityOnAllPath = new ArrayList<RemediationAction>();
                        //For all possible path
                        for (List<InformationSystemHost> anAttackerPath : attackerPath) {

                            RemediationAction remediation = new RemediationAction(ActionType.DEPLOY_SNORT_RULE, costParametersFolder);
                            remediation.setRelatedVertex(leaf);
                            for (Rule rule : rules) {
                                rule.setRule(rule.getRule().replaceFirst("alert", "reject"));
                                remediation.getRemediationParameters().add(rule);
                            }

                            for (InformationSystemHost anAnAttackerPath : anAttackerPath) {
                                //TODO : Add the machine to the "possible machines" only if snort is installed  (need a way to specify this in the input file)
                                //if(attackerPath.get(j).get(incr).getServices().containsKey("snort"))
                                remediation.getPossibleMachines().add(anAnAttackerPath); //The path
                            }

                            detectVulnerabilityOnAllPath.add(remediation);
                        }
                        //Add the detection of vulnerability on all path
                        result.add(detectVulnerabilityOnAllPath);
                    }
                    break;
                }
                case "inCompetent":
                    List<RemediationAction> trainUser = new ArrayList<RemediationAction>();
                    RemediationAction remediation = new RemediationAction(ActionType.TRAIN_USER, costParametersFolder);
                    remediation.setRelatedVertex(leaf);
                    remediation.getRemediationParameters().add(command.params[0]);
                    trainUser.add(remediation);
                    result.add(trainUser);
                    break;
                case "hacl":
                case "haclprimit": {
                    InformationSystemHost from = topology.getHostByNameOrIPAddress(command.params[0]);
                    InformationSystemHost to = topology.getHostByNameOrIPAddress(command.params[1]);
                    List<List<InformationSystemHost>> attackerPath = command.getRoutesBetweenHostsOfHacl(topology);

                    //For HACL Rules, we can block the packets with firewall rules
                    //These rules can be deployed either on the input firewall table or on the output firewall table
                    //It depends of the machines
                    //TODO : things needs to be corrected, to add remediations with "alternating" between INPUT and OUTPUT rules
                    //FIRST LET'S DO THE INPUT RULES

                    List<RemediationAction> blockAttackerOnAllPath = new ArrayList<RemediationAction>();
                    //For all possible path
                    for (List<InformationSystemHost> anAttackerPath1 : attackerPath) {

                        remediation = new RemediationAction(ActionType.DEPLOY_FIREWALL_RULE, costParametersFolder);
                        remediation.setRelatedVertex(leaf);
                        if (command.params[0].equals("internet")) {
                            FirewallRule fwRule = new FirewallRule(Action.DROP, Protocol.getProtocolFromString(command.params[2]), new IPAddress("0.0.0.0"), new IPAddress("0.0.0.0"), new PortRange(true), to.getFirstIPAddress(), new IPAddress("255.255.255.255"), PortRange.fromString(command.params[3]), Table.INPUT);
                            remediation.getRemediationParameters().add(fwRule);
                        } else if (command.params[1].equals("internet")) {
                            FirewallRule fwRule = new FirewallRule(Action.DROP, Protocol.getProtocolFromString(command.params[2]), from.getFirstIPAddress(), new IPAddress("255.255.255.255"), new PortRange(true), new IPAddress("0.0.0.0"), new IPAddress("0.0.0.0"), PortRange.fromString(command.params[3]), Table.INPUT);
                            remediation.getRemediationParameters().add(fwRule);
                        } else {
                            FirewallRule fwRule = new FirewallRule(Action.DROP, Protocol.getProtocolFromString(command.params[2]), from.getFirstIPAddress(), new IPAddress("255.255.255.255"), new PortRange(true), to.getFirstIPAddress(), new IPAddress("255.255.255.255"), PortRange.fromString(command.params[3]), Table.INPUT);
                            remediation.getRemediationParameters().add(fwRule);
                        }
                        //The only thing that change between INPUT and OUTPUT is the machines on which this rule can be deployed
                        for (InformationSystemHost currentAttackerPathHost : anAttackerPath1) {
                            if (command.params[0].equals("internet")) {//Source packet is internet, input will block on all hosts
                                remediation.getPossibleMachines().add(currentAttackerPathHost);
                            } else {
                                InformationSystemHost sourceHost = topology.existingMachineByNameOrIPAddress(command.params[0]);
                                if (sourceHost == null || !sourceHost.equals(currentAttackerPathHost)) {//we add this machine to the possible machines if it is not the sender of the packets
                                    remediation.getPossibleMachines().add(currentAttackerPathHost);
                                }
                            }
                        }

                        blockAttackerOnAllPath.add(remediation);
                    }
                    //Add the block of the attacker on all path
                    result.add(blockAttackerOnAllPath);


                    //THEN LET'S DO THE OUTPUT RULES
                    blockAttackerOnAllPath = new ArrayList<RemediationAction>();
                    //For all possible path
                    for (List<InformationSystemHost> anAttackerPath : attackerPath) {

                        remediation = new RemediationAction(ActionType.DEPLOY_FIREWALL_RULE, costParametersFolder);
                        remediation.setRelatedVertex(leaf);
                        if (command.params[0].equals("internet")) {
                            FirewallRule fwRule = new FirewallRule(Action.DROP, Protocol.getProtocolFromString(command.params[2]), new IPAddress("0.0.0.0"), new IPAddress("0.0.0.0"), new PortRange(true), to.getFirstIPAddress(), new IPAddress("255.255.255.255"), PortRange.fromString(command.params[3]), Table.OUTPUT);
                            remediation.getRemediationParameters().add(fwRule);
                        } else if (command.params[1].equals("internet")) {
                            FirewallRule fwRule = new FirewallRule(Action.DROP, Protocol.getProtocolFromString(command.params[2]), from.getFirstIPAddress(), new IPAddress("255.255.255.255"), new PortRange(true), new IPAddress("0.0.0.0"), new IPAddress("0.0.0.0"), PortRange.fromString(command.params[3]), Table.OUTPUT);
                            remediation.getRemediationParameters().add(fwRule);
                        } else {
                            FirewallRule fwRule = new FirewallRule(Action.DROP, Protocol.getProtocolFromString(command.params[2]), from.getFirstIPAddress(), new IPAddress("255.255.255.255"), new PortRange(true), to.getFirstIPAddress(), new IPAddress("255.255.255.255"), PortRange.fromString(command.params[3]), Table.OUTPUT);
                            remediation.getRemediationParameters().add(fwRule);
                        }

                        //The only thing that change between INPUT and OUTPUT is the machines on which this rule can be deployed
                        for (InformationSystemHost currentAttackerPathHost : anAttackerPath) {
                            if (command.params[1].equals("internet")) {//Source packet is internet, input will block on all hosts
                                remediation.getPossibleMachines().add(currentAttackerPathHost);
                            } else {
                                InformationSystemHost destinationHost = topology.existingMachineByNameOrIPAddress(command.params[1]);
                                if (destinationHost == null || !destinationHost.equals(currentAttackerPathHost)) {//we add this machine to the possible machines if it is not the receiver of the packets
                                    remediation.getPossibleMachines().add(currentAttackerPathHost);
                                }
                            }
                        }

                        blockAttackerOnAllPath.add(remediation);
                    }
                    //Add the block of the attacker on all path
                    result.add(blockAttackerOnAllPath);
                    break;
                }
            }
        }
        return result;

    }

    /**
     * Get the attacker route to access a vulnerability according to the information in the attack path
     *
     * @param leaf     the leaf of the vulnerability
     * @param topology the network topology
     * @return the attacker route
     * @throws Exception
     */
    public List<List<InformationSystemHost>> getAttackerRouteToAVulnerability(Vertex leaf, InformationSystem topology) throws Exception {
        Vertex child = leaf.children.get(0);
        if (child != null) {
            Vertex netAccessVertex = this.getParentOfVertexWithFactCommand(child, "netAccess");
            if (netAccessVertex == null) {
                netAccessVertex = this.getParentOfVertexWithFactCommand(child, "accessMaliciousInput");
            }
            if (netAccessVertex != null) {
                Vertex ruleAccessVertex = netAccessVertex.parents.get(0);
                if (ruleAccessVertex != null) {
                    Vertex haclVertex = this.getParentOfVertexWithFactCommand(ruleAccessVertex, "hacl");
                    if (haclVertex != null) {
                        return haclVertex.fact.datalogCommand.getRoutesBetweenHostsOfHacl(topology);
                    } else
                        throw new Exception("No hacl.");
                } else {
                    throw new Exception("Problem while going up to the hacl for the leaf " + leaf.id);
                }
            } else {
                throw new Exception("Problem while going up to the hacl for the leaf " + leaf.id);
            }
        } else {
            throw new Exception("The leaf a no child");
        }
    }

    /**
     * @param leaf an attack path leaf
     * @return true if the leaf is mandatory to reach the goal of the attack Path
     */
    public boolean leafMandatoryForGoal(Vertex leaf) {
        Vertex goal = this.getGoal();
        if (goal != null) {
            List<Vertex> leaf_list = new ArrayList<Vertex>();
            leaf_list.add(leaf);
            return leavesMandatoryForVertex(leaf_list, goal, new ArrayList<Vertex>());
        }
        return false;
    }

    /**
     * @param leaves a list of leaves
     * @return true if the leaves are mandatory to reach the goal
     */
    public boolean leavesMandatoryForGoal(List<Vertex> leaves) {
        Vertex goal = this.getGoal();
        return goal != null && leavesMandatoryForVertex(leaves, goal, new ArrayList<Vertex>());
    }

    /**
     * Recursive function that test whether or not leaves are mandatory to access a vertex
     *
     * @param leaves the leaves that will be tested
     * @param v      the vertex
     * @return true if the leaves are mandatory / else false
     */
    private boolean leavesMandatoryForVertex(List<Vertex> leaves, Vertex v, List<Vertex> alreadySeen) {
        if (leaves.contains(v))
            return true;
        else if (v.type == VertexType.AND) {
            boolean result = false;
            v.computeParentsAndChildren(this);
            alreadySeen.add(v);
            for (int i = 0; i < v.parents.size(); i++) {
                Vertex parent = v.parents.get(i);
                if (!alreadySeen.contains(parent))
                    result = result || leavesMandatoryForVertex(leaves, parent, alreadySeen);
            }
            alreadySeen.remove(v);
            return result;
        } else if (v.type == VertexType.OR) {
            boolean result = true;
            v.computeParentsAndChildren(this);
            alreadySeen.add(v);
            for (int i = 0; i < v.parents.size(); i++) {
                Vertex parent = v.parents.get(i);
                if (!alreadySeen.contains(parent))
                    result = result && leavesMandatoryForVertex(leaves, parent, alreadySeen);
            }
            alreadySeen.remove(v);
            return result;
        } else {
            return false;
        }
    }

    /**
     * Load the attack path from a DOM element of a XML file (the XML file contains only the arcs, the vertices are in the attack graph)
     *
     * @param root        the DOM element
     * @param attackGraph the corresponding attack graph
     */
    public void loadFromDomElementAndAttackGraph(Element root, AttackGraph attackGraph) {
        Element scoringElement = root.getChild("scoring");
        if (scoringElement != null) {
            this.scoring = Double.parseDouble(scoringElement.getText());
        }

		/* Add all the arcs */
        Element arcs_element = root.getChild("arcs");
        if (arcs_element != null) {
            List<Element> arcs = arcs_element.getChildren("arc");
            for (Element arc_element : arcs) { //All arcs
                Element src_element = arc_element.getChild("dst"); //MULVAL XML FILES INVERSE DESTINATION AND DESTINATION
                Element dst_element = arc_element.getChild("src"); //MULVAL XML FILES INVERSE DESTINATION AND DESTINATION
                if (src_element != null && dst_element != null) {
                    Vertex destination = getVertexFromAttackGraph((int) Double.parseDouble(dst_element.getText()), attackGraph);
                    Vertex source = getVertexFromAttackGraph((int) Double.parseDouble(src_element.getText()), attackGraph);
                    Arc arc = new Arc(source, destination);
                    this.arcs.add(arc);
                }
            }
        }
    }

    /**
     * @param vertexID    the vertex number
     * @param attackGraph an attack graph
     * @return the existing vertex if it is already in the attack path else add this vertex from the attack graph
     */
    public Vertex getVertexFromAttackGraph(int vertexID, AttackGraph attackGraph) {
        if (this.vertices.containsKey(vertexID))
            return this.vertices.get(vertexID);
        else {
            Vertex result = attackGraph.getExistingOrCreateVertex(vertexID);
            this.vertices.put(vertexID, result);
            return result;
        }
    }

    /**
     * @return the dom element corresponding to this attack path XML file
     */
    public Element toDomXMLElement() {
        Element root = new Element("attack_path");

        Element scoringElement = new Element("scoring");
        scoringElement.setText(this.scoring + "");
        root.addContent(scoringElement);
        //arcs
        Element arcsElement = new Element("arcs");
        root.addContent(arcsElement);
        for (Arc arc : arcs) {
            Element arcElement = new Element("arc");
            arcsElement.addContent(arcElement);
            Element srcElement = new Element("src");
            srcElement.setText(arc.destination.id + "");
            arcElement.addContent(srcElement);
            Element dstElement = new Element("dst");
            dstElement.setText(arc.source.id + "");
            arcElement.addContent(dstElement);
        }

        return root;
    }

    @Override
    public String toString() {
        String result = "AttackPath : ";
        for (int i : this.vertices.keySet()) {
            result += this.vertices.get(i).id + " - ";
        }
        return result;
    }

    /**
     * @param informationSystem the information system
     * @return The topology Graph associated to this attack path
     * @throws Exception
     */
    public InformationSystemGraph getRelatedTopologyGraph(InformationSystem informationSystem) throws Exception {
        InformationSystemGraph result = super.getRelatedTopologyGraph(informationSystem);

        result.addTarget(this.getGoal().getRelatedMachine(informationSystem));

        return result;
    }

    @Override
    public AttackPath clone() throws CloneNotSupportedException {
        AttackPath copie = (AttackPath) super.clone();

        if (this.goal != null)
            copie.goal = copie.vertices.get(this.goal.id);

        return copie;
    }

    public static class AttackPathComparator implements Comparator<AttackPath> {
        public int compare(AttackPath a1, AttackPath a2) {
            //descending order
            if (a1.scoring == a2.scoring) {
                return 0;
            } else if (a1.scoring < a2.scoring) {
                return 1;
            } else {
                return -1;
            }
        }
    }

    /**
     * Comparator to compare deployable remediation according to their cost
     */
    private class DeployableRemediationComparator implements Comparator<DeployableRemediation> {
        public int compare(DeployableRemediation dr1, DeployableRemediation dr2) {
            if (dr1.getCost() == dr2.getCost()) {
                return 0;
            } else if (dr1.getCost() < dr2.getCost()) {
                return -1;
            } else {
                return 1;
            }
        }
    }

} public class MulvalAttackGraph extends AttackGraph implements Cloneable {

    /**
     * The path to the xml path of the attack graph
     */
    public String xmlFilePath = "";

    public MulvalAttackGraph() {

    }

    /**
     * Create an attack graph from an output xml file from Mulval
     *
     * @param xmlPath The path to the xml file
     * @throws Exception
     */
    public MulvalAttackGraph(String xmlPath) throws Exception {
        this.loadFromFile(xmlPath);
    }

    /**
     * Load the attack graph from the XML file generated by MulVAL
     * @param xmlPath the path of the XML file
     * @throws Exception
     */
    public void loadFromFile(String xmlPath) throws Exception {
        this.xmlFilePath = xmlPath;
        FileInputStream file = new FileInputStream(xmlPath);

        SAXBuilder sxb = new SAXBuilder();
        Document document = sxb.build(file);
        Element root = document.getRootElement();

        addArcsAndVerticesFromDomElement(root);

    }

    /**
     * Create the attack graph from the XML DOM element
     * @param root
     */
    public void addArcsAndVerticesFromDomElement(Element root) {
        if (root == null)
            return;

		/* Add all the vertices */
        Element vertices_element = root.getChild("vertices");
        if (vertices_element != null) {
            List<Element> vertices = vertices_element.getChildren("vertex");
            for (Element vertex_element : vertices) { //All arcs
                Element id_element = vertex_element.getChild("id");
                if (id_element != null && Integer.parseInt(id_element.getText()) > 0) {
                    int id_vertex = Integer.parseInt(id_element.getText());
                    Vertex vertex = getExistingOrCreateVertex(id_vertex);
                    Element fact_element = vertex_element.getChild("fact");
                    if (fact_element != null) {
                        vertex.fact = new Fact(fact_element.getText(), vertex);
                    }

                    Element metric_element = vertex_element.getChild("metric");
                    if (metric_element != null) {
                        vertex.mulvalMetric = Float.parseFloat(metric_element.getText());
                    }

                    Element type_element = vertex_element.getChild("type");
                    if (type_element != null) {
                        vertex.setType(type_element.getText());
                    }
                }
            }
        }

		/* Add all the arcs */
        Element arcs_element = root.getChild("arcs");
        if (arcs_element != null) {
            List<Element> arcs = arcs_element.getChildren("arc");
            for (Element arc_element : arcs) { //All arcs
                Element src_element = arc_element.getChild("dst"); //MULVAL XML FILES INVERSE DESTINATION AND DESTINATION
                Element dst_element = arc_element.getChild("src"); //MULVAL XML FILES INVERSE DESTINATION AND DESTINATION
                if (src_element != null && dst_element != null) {
                    Vertex destination = getExistingOrCreateVertex(Integer.parseInt(dst_element.getText()));
                    Vertex source = getExistingOrCreateVertex(Integer.parseInt(src_element.getText()));
                    Arc arc = new Arc(source, destination);
                    this.arcs.add(arc);
                }
            }
        }
    }

    @Override
    public MulvalAttackGraph clone() throws CloneNotSupportedException {
        return (MulvalAttackGraph) super.clone();
    }
} public class Vertex implements Cloneable {
    /**
     * Identifier of the Vertex
     */
    public int id = 0;
    /**
     * The fact of the vertex
     */
    public Fact fact = null;
    /**
     * A metric
     */
    public float mulvalMetric = 0;
    /**
     * The vertex type
     */
    public VertexType type;
    /**
     * The vertex parents
     */
    public List<Vertex> parents = new ArrayList<Vertex>();
    /**
     * The vertex children
     */
    public List<Vertex> children = new ArrayList<Vertex>();
    /**
     * The impact metrics of this vertex
     */
    public List<ImpactMetric> impactMetrics = new ArrayList<ImpactMetric>();
    /**
     * The related vulnerability if this vertex contains a vulnerability
     */
    public Vulnerability relatedVulnerabilibty = null;
    /**
     * The machine represented by the vertex (if appropriate)
     */
    public InformationSystemHost concernedMachine = null;
    /**
     * The service represented by the vertex (if appropriate)
     */
    public Service concernedService = null;

    /**
     * Create a vertex from an id
     *
     * @param id the index of the vertex
     */
    public Vertex(int id) {
        this.id = id;
    }

    /**
     * Set the type of the vertex from a string
     *
     * @param type the type in string
     */
    public void setType(String type) {
        switch (type) {
            case "AND":
                this.type = VertexType.AND;
                break;
            case "OR":
                this.type = VertexType.OR;
                break;
            case "LEAF":
                this.type = VertexType.LEAF;
                break;
        }
    }

    /**
     * Compute the parents of the vertex
     * @param graph the complete attack graph
     */
    public void computeParentsAndChildren(AttackGraph graph) {
        this.parents = new ArrayList<Vertex>();//initialize the list of parents
        this.children = new ArrayList<Vertex>();//initialize the list of children
        for (int i = 0; i < graph.arcs.size();i++) {
            Arc arc = graph.arcs.get(i);
            if(arc.destination == this) {
                this.parents.add(arc.source);
            }
            else if(arc.source == this) {
                this.children.add(arc.destination);
            }
        }
    }

    /**
     * Get the machine which is referenced by this vertex of the attack graph
     * @param informationSystem the information system in which is the machine to find
     * @return the machine of the information system referenced by this vertex
     * @throws Exception
     */
    public InformationSystemHost getRelatedMachine(InformationSystem informationSystem) throws Exception {
        if (concernedMachine != null)
            return concernedMachine;
        InformationSystemHost result = null;
        if (this.fact != null) {
            if (this.fact.type == Fact.FactType.DATALOG_FACT && this.fact.datalogCommand != null) {
                DatalogCommand command = this.fact.datalogCommand;
                switch (command.command) {
                    case "vulExists":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[0]);
                        }
                        break;
                    case "execCode":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[0]);
                        }
                        break;
                    case "netAccess":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[0]);
                        }
                        break;
                    case "canAccessHost":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[0]);
                        }
                        break;
                    case "hacl":
                        if (command.params.length >= 2) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[1]);
                        }
                        break;
                    case "accessMaliciousInput":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[0]);
                        }
                        break;
                    case "networkServiceInfo":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[0]);
                        }
                        break;
                    case "principalCompromised":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByUserName(command.params[0]);
                        }
                        break;
                    case "attackerLocated":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[0]);
                        }
                        break;
                    case "accessFile":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[0]);
                        }
                        break;
                    case "hasAccount":
                        if (command.params.length >= 1) {
                            result = informationSystem.existingMachineByNameOrIPAddress(command.params[1]);
                        }
                        break;
                }
            }
        }
        this.concernedMachine = result;
        return result;
    }

    @Override
    public Vertex clone() throws CloneNotSupportedException {
        Vertex copie = (Vertex) super.clone();
        copie.type = this.type;
        copie.fact = this.fact.clone();
        copie.fact.attackGraphVertex = copie;
        copie.relatedVulnerabilibty = this.relatedVulnerabilibty;
        copie.concernedMachine = this.concernedMachine;
        copie.concernedService = this.concernedService;
        copie.children = new ArrayList<Vertex>();
        copie.parents = new ArrayList<Vertex>();
        return copie;
    }

    @Override
    public String toString() {
        /*return "Vertex [fact=" + fact + ", id=" + id + ", metric=" + metric
				+ ", type=" + type + "]";*/
        return this.id + ":" + this.fact.factString;
    }

    /**
     * @param isRule         true if the looked for child is a rule, false if it is a datalog command
     * @param ruleOrFactType the content of the command or of the rule
     * @return the child if it exists, else null
     */
    public Vertex childOfType(boolean isRule, String ruleOrFactType) {
        for (Vertex child : this.children) {
            if (child != null && child.fact != null) {
                if (isRule && child.fact.type == Fact.FactType.RULE) { //A RULE
                    if (child.fact.factRule != null && child.fact.factRule.ruleText != null && child.fact.factRule.ruleText.contains(ruleOrFactType))
                        return child;
                } else if (!isRule && child.fact.type == Fact.FactType.DATALOG_FACT) { // A datalog fact
                    if (child.fact.datalogCommand != null && child.fact.datalogCommand.command != null && child.fact.datalogCommand.command.contains(ruleOrFactType))
                        return child;
                }
            }
        }

        return null;
    }

    /**
     * @param isRule         true if the looked for parent is a rule, false if it is a datalog command
     * @param ruleOrFactType the content of the command or of the rule
     * @return the parent if it exists, else null
     */
    public Vertex parentOfType(boolean isRule, String ruleOrFactType) {
        for (Vertex parent : this.parents) {
            if (parent != null && parent.fact != null) {
                if (isRule && parent.fact.type == Fact.FactType.RULE) { //A RULE
                    if (parent.fact.factRule != null && parent.fact.factRule.ruleText != null && parent.fact.factRule.ruleText.contains(ruleOrFactType))
                        return parent;
                } else if (!isRule && parent.fact.type == Fact.FactType.DATALOG_FACT) { // A datalog fact
                    if (parent.fact.datalogCommand != null && parent.fact.datalogCommand.command != null && parent.fact.datalogCommand.command.contains(ruleOrFactType))
                        return parent;
                }
            }
        }

        return null;
    }

    /**
     * Represent the possible type of vertices
     */
    public static enum VertexType {
        AND, OR, LEAF
    }
} public class Database {
    /**
     * Time out to load the database
     */
    private final int timeout = 30;
    /**
     * The jdbc connection
     */
    private Connection conn;

    /**
     * Constructor of Database class
     *
     * @param path the path of the sqlite database
     * @throws Exception if problem to create the database
     */
    public Database(String path) throws Exception {
        super();
        loadFromFile(path);
    }

    /**
     * Load the sqlite database from a file
     *
     * @param path the path of the sqlite file to load
     * @throws Exception
     */
    public void loadFromFile(String path) throws Exception {

        // register the driver
        String sDriverName = "org.sqlite.JDBC";
        Class.forName(sDriverName);

        // now we set up a set of fairly basic string variables to use in the body of the code proper
        String sJdbc = "jdbc:sqlite";
        String sDbUrl = sJdbc + ":" + path;

        // create a database connection
        this.conn = DriverManager.getConnection(sDbUrl);

    }

    /**
     * Close the connection to the database
     */
    public void finalize() {
        try {
            getConn().close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * SQL instructions to create the tables of the database
     *
     * @throws Exception
     */
    public void createDB() throws Exception {
        executeQuery("CREATE TABLE IF NOT EXISTS vulnerability (id INTEGER PRIMARY KEY AUTOINCREMENT, cve TEXT UNIQUE, description TEXT, cvss_id INTEGER)");
        executeQuery("CREATE TABLE IF NOT EXISTS rules (id INTEGER PRIMARY KEY AUTOINCREMENT, rule TEXT,description TEXT, sid INTEGER, gid INTEGER);");
        executeQuery("CREATE TABLE IF NOT EXISTS rules_vulnerability (id_rule INTEGER, id_vulnerability INTEGER);");
        executeQuery("CREATE UNIQUE INDEX IF NOT EXISTS index_rules_vulnerability ON rules_vulnerability (id_rule ASC, id_vulnerability ASC);");
        executeQuery("CREATE TABLE IF NOT EXISTS patchs (id INTEGER PRIMARY KEY AUTOINCREMENT, link TEXT, description TEXT);");
        executeQuery("CREATE TABLE IF NOT EXISTS patchs_vulnerability (id_patch INTEGER, id_vulnerability INTEGER);");
        executeQuery("CREATE UNIQUE INDEX IF NOT EXISTS index_patchs_vulnerability ON patchs_vulnerability (id_patch ASC, id_vulnerability ASC);");
        executeQuery("CREATE TABLE IF NOT EXISTS cre (id INTEGER PRIMARY KEY AUTOINCREMENT, cre_id TEXT, id_vulnerability INTEGER, id_remediation_type INTEGER , id_remediation INTEGER);");
        executeQuery("CREATE UNIQUE INDEX IF NOT EXISTS index_cre ON cre (id_remediation ASC, id_vulnerability ASC, id_remediation_type ASC);");
        executeQuery("CREATE TABLE IF NOT EXISTS cvss (id INTEGER PRIMARY KEY AUTOINCREMENT, score REAL, access_vector TEXT, access_complexity TEXT , authentication TEXT, confidentiality_impact TEXT, integrity_impact TEXT, availability_impact TEXT);");
        executeQuery("CREATE UNIQUE INDEX IF NOT EXISTS index_cvss ON cvss (id ASC);");
        executeQuery("CREATE TABLE IF NOT EXISTS cpe (id INTEGER PRIMARY KEY AUTOINCREMENT, cpe_id TEXT);");
        executeQuery("CREATE TABLE IF NOT EXISTS cpe_vulnerability (id_cpe INTEGER, id_vulnerability INTEGER);");
        executeQuery("CREATE UNIQUE INDEX IF NOT EXISTS index_cpe_vulnerability ON cpe_vulnerability (id_cpe ASC, id_vulnerability ASC);");

    }

    /**
     * function to execute a simple query
     *
     * @param query the query to execute
     * @throws java.sql.SQLException
     */
    public void executeQuery(String query) throws SQLException {
        Statement stmt = getConn().createStatement();
        stmt.setQueryTimeout(timeout);
        stmt.executeUpdate(query);
        stmt.close();
    }

    /**
     * @return the connection element
     */
    public Connection getConn() {
        return conn;
    }
} public class InformationSystem implements Cloneable {


    /**
     * The network topology of the information system
     */
    private Topology topology = new Topology();

    /**
     * Is the attacker located on the internet in addition to {@link #machinesOfAttacker} ?
     */
    private boolean attackerLocatedOnInternet = false;

    /**
     * The flow matrix of the Information System
     */
    private FlowMatrix flowMatrix;

    /**
     * The list of machines that are mastered by the attacker
     */
    private List<InformationSystemHost> machinesOfAttacker = new ArrayList<InformationSystemHost>();


    /**
     * Create an empty information system
     */
    public InformationSystem() {

    }

    @Override
    public InformationSystem clone() throws CloneNotSupportedException {
        InformationSystem copie = (InformationSystem) super.clone();
        copie.topology = copie.topology.clone();
        return copie;
    }

    /**
     * Create a file with Datalog rules for the input of Mulval
     *
     * @param mulvalFilePath the filePath to store MulVAL input Datalog file
     * @throws Exception
     */
    public void exportToMulvalDatalogFile(String mulvalFilePath) throws Exception {
        PrintWriter fichier = new PrintWriter(new BufferedWriter(new FileWriter(mulvalFilePath)));

        //Add internet
        fichier.println("/**********************************/");
        fichier.println("/*    Add Internet                */");
        fichier.println("/**********************************/");

        fichier.println("attackerLocated(internet_host).");
        fichier.println("hasIP(internet_host,'1.1.1.1').");
        fichier.println("defaultLocalFilteringBehavior('internet_host',allow).");
        fichier.println("isInVlan('1.1.1.1','internet').");
        fichier.println();

        for (Host host : topology.getHosts()) {
            fichier.println("/**********************************/");
            fichier.println("/*    Add Host " + host.getName() + " */");
            fichier.println("/**********************************/");
            InformationSystemHost informationSystemHost = (InformationSystemHost) host;
            fichier.println("attackerLocated('" + host.getName() + "').");
            fichier.println("attackGoal(execCode('" + host.getName() + "', _)).");
            for (Interface networkInterface : host.getInterfaces().values()) {
                fichier.println("hasIP('" + host.getName() + "','" + networkInterface.getAddress().toString() + "').");
                if (networkInterface.getVlan() != null && !networkInterface.getVlan().getName().isEmpty()) {
                    fichier.println("isInVlan('" + networkInterface.getAddress().toString() + "','" + networkInterface.getVlan().getName() + "').");
                }
            }
            fichier.println("hostAllowAccessToAllIP('" + host.getName() + "').");
            for (Service service : informationSystemHost.getServices().values()) {
                fichier.println("installed('" + host.getName() + "','" + service.getName() + "').");
                if (service.getPortNumber() != 0) {
                    fichier.println("networkServiceInfo('" + service.getIpAddress() + "', '" + service.getName() + "', '" + service.getProtocol().toString() + "', " + service.getPortNumber() + ", 'user').");
                }

                for (String cve : service.getVulnerabilities().keySet()) {
                    Vulnerability vulnerability = service.getVulnerabilities().get(cve);
                    fichier.println("vulProperty('" + vulnerability.cve + "', " + vulnerability.exploitType + ", " + vulnerability.exploitGoal + ").");
                    fichier.println("vulExists('" + host.getName() + "', '" + vulnerability.cve + "', '" + service.getName() + "', " + vulnerability.exploitType + ", " + vulnerability.exploitGoal + ").");
                    if (vulnerability.cvss != null && vulnerability.cvss.getScore() >= 6.6) {
                        fichier.println("cvss('" + vulnerability.cve + "',h).");
                    } else if (vulnerability.cvss != null && vulnerability.cvss.getScore() >= 3.3) {
                        fichier.println("cvss('" + vulnerability.cve + "',m).");
                    } else if (vulnerability.cvss != null && vulnerability.cvss.getScore() > 0) {
                        fichier.println("cvss('" + vulnerability.cve + "',l).");
                    } else {
                        fichier.println("cvss('" + vulnerability.cve + "',m).");
                    }
                }
            }
            fichier.println();

        }

        //Add flow matrix elements
        if (this.flowMatrix != null && this.flowMatrix.getFlowMatrixLines().size() > 0) {
            for (FlowMatrixLine flowMatrixLine : this.flowMatrix.getFlowMatrixLines()) {
                String mulvalDestinationPort;
                if (flowMatrixLine.getDestination_port().isAny()) {
                    mulvalDestinationPort = "_";
                } else if (flowMatrixLine.getDestination_port().getMax() == flowMatrixLine.getDestination_port().getMin()) {
                    mulvalDestinationPort = "" + flowMatrixLine.getDestination_port().getMax();
                } else {
                    throw new IllegalStateException("Minimum and Maximum port range are not yet managed.");
                }

                String mulvalProtocol;
                if (flowMatrixLine.getProtocol().equals(FirewallRule.Protocol.ANY)) {
                    mulvalProtocol = "_";
                } else {
                    mulvalProtocol = "'" + flowMatrixLine.getProtocol().toString() + "'";
                }

                if (!mulvalDestinationPort.isEmpty() && !mulvalProtocol.isEmpty()) {
                    FlowMatrixElement sourceElement = flowMatrixLine.getSource();
                    FlowMatrixElement destinationElement = flowMatrixLine.getDestination();

                    if (sourceElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.IP) &&
                            destinationElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.IP)) {
                        if (sourceElement.getResource() instanceof Interface && destinationElement.getResource() instanceof Interface) {
                            Interface sourceInterface = (Interface) sourceElement.getResource();
                            Interface destinationInterface = (Interface) destinationElement.getResource();
                            fichier.println("haclprimit('" + sourceInterface.getAddress() + "','" + destinationInterface.getAddress() + "', " + mulvalDestinationPort + "," + mulvalProtocol + ").");
                        } else {
                            throw new IllegalStateException("Illegal resource type");
                        }
                    } else if (sourceElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.INTERNET) &&
                            destinationElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.IP)) {
                        if (destinationElement.getResource() instanceof Interface) {
                            Interface destinationInterface = (Interface) destinationElement.getResource();
                            fichier.println("vlanToIP('internet','" + destinationInterface.getAddress() + "', " + mulvalDestinationPort + "," + mulvalProtocol + ").");
                        } else {
                            throw new IllegalStateException("Illegal resource type");
                        }
                    } else if (sourceElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.VLAN) &&
                            destinationElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.IP)) {
                        if (sourceElement.getResource() instanceof VLAN && destinationElement.getResource() instanceof Interface) {
                            VLAN sourceVlan = (VLAN) sourceElement.getResource();
                            Interface destinationInterface = (Interface) destinationElement.getResource();
                            fichier.println("vlanToIP('" + sourceVlan.getName() + "','" + destinationInterface.getAddress() + "', " + mulvalDestinationPort + "," + mulvalProtocol + ").");
                        } else {
                            throw new IllegalStateException("Illegal resource type");
                        }
                    } else if (sourceElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.IP) &&
                            destinationElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.VLAN)) {
                        if (sourceElement.getResource() instanceof Interface && destinationElement.getResource() instanceof VLAN) {
                            Interface sourceInterface = (Interface) sourceElement.getResource();
                            VLAN destinationVlan = (VLAN) destinationElement.getResource();
                            fichier.println("ipToVlan('" + sourceInterface.getAddress() + "','" + destinationVlan.getName() + "', " + mulvalDestinationPort + "," + mulvalProtocol + ").");
                        } else {
                            throw new IllegalStateException("Illegal resource type");
                        }
                    } else if (sourceElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.INTERNET) &&
                            destinationElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.VLAN)) {
                        if (destinationElement.getResource() instanceof VLAN) {
                            VLAN destinationVLAN = (VLAN) destinationElement.getResource();
                            fichier.println("vlanToVlan('internet','" + destinationVLAN.getName() + "', " + mulvalDestinationPort + "," + mulvalProtocol + ").");
                        } else {
                            throw new IllegalStateException("Illegal resource type");
                        }
                    } else if (sourceElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.VLAN) &&
                            destinationElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.VLAN)) {
                        if (sourceElement.getResource() instanceof VLAN && destinationElement.getResource() instanceof VLAN) {
                            VLAN sourceVLAN = (VLAN) sourceElement.getResource();
                            VLAN destinationVLAN = (VLAN) destinationElement.getResource();
                            fichier.println("vlanToVlan('" + sourceVLAN.getName() + "','" + destinationVLAN.getName() + "', " + mulvalDestinationPort + "," + mulvalProtocol + ").");
                        } else {
                            throw new IllegalStateException("Illegal resource type");
                        }
                    } else if (sourceElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.IP) &&
                            destinationElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.INTERNET)) {
                        if (sourceElement.getResource() instanceof Interface) {
                            Interface sourceInterface = (Interface) sourceElement.getResource();
                            fichier.println("ipToVlan('" + sourceInterface.getAddress() + "','internet', " + mulvalDestinationPort + "," + mulvalProtocol + ").");
                        } else {
                            throw new IllegalStateException("Illegal resource type");
                        }
                    } else if (sourceElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.VLAN) &&
                            destinationElement.getType().equals(FlowMatrixElement.FlowMatrixElementType.INTERNET)) {
                        if (sourceElement.getResource() instanceof VLAN) {
                            VLAN sourceVLAN = (VLAN) sourceElement.getResource();
                            fichier.println("vlanToVlan('" + sourceVLAN.getName() + "','internet', " + mulvalDestinationPort + "," + mulvalProtocol + ").");
                        } else {
                            throw new IllegalStateException("Illegal resource type");
                        }
                    } else {
                        throw new IllegalStateException("Unknown values of flow matrix");
                    }


                } else {
                    throw new IllegalStateException("Empty port or protocol");
                }
            }
        } else { //Empty flow matrix, add access betwen each couples of VLANs
            for (VLAN vlanFrom : this.topology.getVlans().values()) {
                for (VLAN vlanTo : this.topology.getVlans().values()) {
                    if (!vlanFrom.equals(vlanTo)) {
                        fichier.println("vlanToVlan('" + vlanFrom.getName() + "','" + vlanTo.getName() + "',_,_).");
                    }

                }
            }
        }

        fichier.println("/**********************************/");
        fichier.println("/******     General Rules    ******/");
        fichier.println("/**********************************/");
        fichier.println("defaultLocalFilteringBehavior(_,allow)."); //Attacker is on the internet

        fichier.close();
    }

    /**
     * Get a machine by its name or IP address. If the machine doesn't exist, add it to the topology
     *
     * @param str the name or IP address of the machine
     * @return the created or existing machine.
     * @throws Exception
     */
    public InformationSystemHost getHostByNameOrIPAddress(String str) throws Exception {
        if (IPAddress.isAnIPAddress(str)) {
            return getMachineByIPAddress(new IPAddress(str));
        } else {
            InformationSystemHost existingMachine = existingMachineByName(str);
            if (existingMachine != null)
                return existingMachine;
            InformationSystemHost newMachine = new InformationSystemHost(str, this.topology);
            this.topology.getHosts().add(newMachine);
            return newMachine;
        }
    }

    /**
     * get the routes from the internet to a host
     *
     * @param to the destination host
     * @return The list of routes
     * @throws Exception
     */
    public List<List<InformationSystemHost>> routesFromInternetTo(Host to) throws Exception {
        List<List<InformationSystemHost>> result = new ArrayList<List<InformationSystemHost>>();
        List<List<Host>> routes = to.getRoutesFromInternet();

        for (List<Host> route : routes) {
            List<InformationSystemHost> tmpResult = new ArrayList<InformationSystemHost>();
            for (Host aRoute : route) {
                tmpResult.add((InformationSystemHost) aRoute);
            }
            result.add(tmpResult);
        }

        return result;
    }

    /**
     * Get an existing machine of the information system with its name
     *
     * @param name the name of the machine
     * @return the machine if it exists else null
     */
    public InformationSystemHost existingMachineByName(String name) {
        for (int i = 0; i < this.topology.getHosts().size(); i++) {
            if (this.topology.getHosts().get(i).getName().equals(name))
                return (InformationSystemHost) this.topology.getHosts().get(i);
        }
        if (name.equals("internet"))
            return new InformationSystemHost("internet", topology);

        return null;
    }

    /**
     * Get an existing machine of the information system with its name or ip address
     *
     * @param str the name or ip address of the machine
     * @return the machine if it exists else null
     * @throws Exception
     */
    public InformationSystemHost existingMachineByNameOrIPAddress(String str) throws Exception {
        if (IPAddress.isAnIPAddress(str))
            return (InformationSystemHost) topology.existingHostByIPAddress(new IPAddress(str));
        else
            return existingMachineByName(str);
    }


    /**
     * Get an existing machine of the information system with a specific user
     *
     * @param username the name of the user
     * @return an existing machine that have the user that can use it.
     */
    public InformationSystemHost existingMachineByUserName(String username) {
        for (int i = 0; i < this.topology.getHosts().size(); i++) {
            InformationSystemHost machine = (InformationSystemHost) this.topology.getHosts().get(i);
            for (String j : machine.getUsers().keySet()) {
                if (machine.getUsers().get(j).getName().equals(username))
                    return machine;
            }
        }
        return null;
    }

    /**
     * Get a machine by its IP address, or create a new one if it does not exist
     *
     * @param ipAddress an IP Address
     * @return The machine in the topology that has this IP Address. If this machine doesn't exists, just add a new one.
     * @throws Exception
     */
    public InformationSystemHost getMachineByIPAddress(IPAddress ipAddress) throws Exception {
        InformationSystemHost existingMachine = (InformationSystemHost) topology.existingHostByIPAddress(ipAddress);
        if (existingMachine != null)
            return existingMachine;
        InformationSystemHost newMachine = new InformationSystemHost(ipAddress.getAddress(), topology);
        newMachine.addInterface("int1", ipAddress.getAddress());
        this.topology.getHosts().add(newMachine);
        return newMachine;
    }

    /**
     * Get the route from a host to the internet
     *
     * @param from the start of the route.
     * @return the route from a host to the internet
     * @throws Exception
     */
    public List<InformationSystemHost> routeToInternetFrom(Host from) throws Exception {
        List<InformationSystemHost> result = new ArrayList<InformationSystemHost>();
        List<Host> hosts = from.getRouteToInternet();
        for (Host host : hosts) {
            result.add((InformationSystemHost) host);
        }
        return result;
    }

    /**
     * Get the route between two hosts
     *
     * @param from the start of the route
     * @param to   the destination host
     * @return the route between two hosts
     * @throws Exception
     */
    public List<InformationSystemHost> routeBetweenHosts(InformationSystemHost from, InformationSystemHost to) throws Exception {
        List<InformationSystemHost> result = new ArrayList<InformationSystemHost>();
        List<Host> route = topology.routeBetweenHosts(from, to);
        for (Host aRoute : route) {
            result.add((InformationSystemHost) aRoute);
        }
        return result;
    }


    /**
     * Save the attack graph in an xml file
     *
     * @param filePath the file path where the attack graph will be save
     * @throws Exception
     */
    public void saveToXmlFile(String filePath) throws Exception {
        XMLOutputter output = new XMLOutputter(Format.getPrettyFormat());
        output.output(toDomXMLElement(), new FileOutputStream(filePath));
    }

    /**
     * Get the XML DOM element of this information system
     *
     * @return the dom element corresponding to this topology with the format of the tva report file
     */
    public Element toDomXMLElement() {
        Element root = new Element("topology");

        //machines
        for (int i = 0; i < this.topology.getHosts().size(); i++) {
            InformationSystemHost machine = (InformationSystemHost) this.topology.getHosts().get(i);
            root.addContent(machine.toDomXMLElement());
        }

        return root;
    }

    /**
     * Load a network topology from a dom element
     *
     * @param domElement the dom element of an xml file
     * @throws Exception
     */
    public void loadFromDomElement(Element domElement, Database db) throws Exception {
        if (domElement == null)
            return;
        List<Element> hostsElement = domElement.getChildren("machine");
        for (Element hostElement : hostsElement) {
            InformationSystemHost host = new InformationSystemHost(this.topology);
            host.loadFromDomElement(hostElement, this.topology, db);
            this.topology.getHosts().add(host);
        }
        this.flowMatrix = new FlowMatrix(domElement.getChild("flow-matrix"), this.topology);
    }


    /**
     * Load the topology from an xml file
     *
     * @param XMLFilePath the path to the xml file
     * @throws Exception
     */
    public void loadFromXMLFile(String XMLFilePath, Database db) throws Exception {
        FileInputStream file = new FileInputStream(XMLFilePath);
        SAXBuilder sxb = new SAXBuilder();
        Document document = sxb.build(file);
        Element root = document.getRootElement();

        this.loadFromDomElement(root, db);
    }

    /**
     * Generates the Json object relative to the hosts list
     * @return the Json Object containing the hosts list
     */
    public JSONObject getHostsListJson() {
        //Build the json list of hosts
        JSONObject json = new JSONObject();
        JSONArray hosts_array = new JSONArray();
        for (Host host : this.getTopology().getHosts()) {
            InformationSystemHost informationSystemHost = (InformationSystemHost) host;
            JSONObject host_object = new JSONObject();
            host_object.put("name", informationSystemHost.getName());
            JSONArray security_requirements_array = new JSONArray();
            for (SecurityRequirement securityRequirement : informationSystemHost.getSecurityRequirements()) {
                JSONObject security_requirement = new JSONObject();
                security_requirement.put("name", securityRequirement.getName());
                security_requirement.put("metric", securityRequirement.getMetricPlainText());
                security_requirements_array.put(security_requirement);
            }
            host_object.put("security_requirements", security_requirements_array);
            hosts_array.put(host_object);
        }
        json.put("hosts", hosts_array);
        return json;
    }

    /**
     * Get the network topology
     *
     * @return the topology
     */
    public Topology getTopology() {
        return topology;
    }

    /**
     * Is the attacker located on the internet in addition to {@link #machinesOfAttacker} ?
     */
    public boolean isAttackerLocatedOnInternet() {
        return attackerLocatedOnInternet;
    }

    /**
     * Set the attacker on the internet status
     *
     * @param attackerLocatedOnInternet the new value of attackerLocatedOnInternet
     */
    public void setAttackerLocatedOnInternet(boolean attackerLocatedOnInternet) {
        this.attackerLocatedOnInternet = attackerLocatedOnInternet;
    }

    /**
     * Get the list of machines where the attacker is located (in addition to the internet)
     */
    public List<InformationSystemHost> getMachinesOfAttacker() {
        return machinesOfAttacker;
    }

    /**
     * Set the list of machines of the internet
     *
     * @param machinesOfAttacker new machines of the attacker
     */
    public void setMachinesOfAttacker(List<InformationSystemHost> machinesOfAttacker) {
        this.machinesOfAttacker = machinesOfAttacker;
    }
} public class ProjectProperties {
    public static final String PROPERTY_FILE_NAME = System.getProperty("user.home") + "/.remediation/config.properties";

    /**
     * Load the property file from the file define in the constant {@link #PROPERTY_FILE_NAME}
     *
     * @return the {@link java.util.Properties} object
     */
    private static Properties loadPropertyFile() {
        File file = new File(PROPERTY_FILE_NAME);
        if (!file.exists()) {
            new File(file.getParent()).mkdirs();//If folder doesn't exist, create it
            System.err.println("Property file does not exists, I will create a new one in " + PROPERTY_FILE_NAME);
            Properties prop = new Properties();
            try {
                prop.store(new FileOutputStream(PROPERTY_FILE_NAME), null);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return prop;
        } else {
            Properties prop = new Properties();
            try {

                prop.load(new FileInputStream(file));
            } catch (Exception e) {
                e.printStackTrace();
            }

            return prop;
        }
    }

    /**
     * Get a property from the property file
     *
     * @param propertyName the property name
     * @return the corresponding property
     */
    public static String getProperty(String propertyName) {
        Properties prop = loadPropertyFile();
        return prop.getProperty(propertyName);

    }

    /**
     * Change a property value
     *
     * @param propertyName name of the property
     * @param value        new value
     */
    public static void setProperty(String propertyName, String value) {
        Properties prop = loadPropertyFile();
        prop.setProperty(propertyName, value);
        try {
            prop.store(new FileOutputStream(PROPERTY_FILE_NAME), null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} public class DeployableRemediation {
    /**
     * The actions of the deployable remediation
     */
    private List<DeployableRemediationAction> actions = new ArrayList<DeployableRemediationAction>();

    /**
     * The cost of the deployable remediation
     */
    private double cost = 0;

    /**
     * The corrected attack path
     */
    private AttackPath correctedPath;

    /**
     * The information system
     */
    private InformationSystem informationSystem;

    /**
     * Create a deployable remediation with the attack path it is correcting
     *
     * @param correctedPath the corrected attack path.
     */

    public DeployableRemediation(AttackPath correctedPath, InformationSystem informationSystem) {
        this.correctedPath = correctedPath;
        this.informationSystem = informationSystem;
    }

    /**
     * Compute the cost of the deployable list of remediations
     *
     * @return the cost of this deployable remediation
     * @throws Exception the exception
     */
    public double computeCost() throws Exception {
        if (getCost() == 0) {
            for (int i = 0; i < getActions().size(); i++) {
                setCost(getCost() + getActions().get(i).getRemediationAction().getOperationalCost());
            }
        }
        return getCost();
    }

    /**
     * To XML element.
     *
     * @return the dom element corresponding to this deployable remediation
     */
    public Element toXMLElement() {
        Element root = new Element("remediation");

        Element costElement = new Element("cost");
        costElement.setText(this.getCost() + "");
        root.addContent(costElement);

        Element habitIndexElement = new Element("habit_index");
        habitIndexElement.setText(this.getHabitIndex() + "");
        root.addContent(habitIndexElement);

        //actions
        Element actionsElement = new Element("remediation_actions");
        root.addContent(actionsElement);
        for (int i = 0; i < getActions().size(); i++) {
            DeployableRemediationAction action = getActions().get(i);

            actionsElement.addContent(action.toXMLElement());
        }

        return root;
    }

    private double getHabitIndex() {
        Logger.getAnonymousLogger().log(Level.INFO, this.actions.toString() + " has been validated to correct the path.");
        String remediationsHistoryPath = ProjectProperties.getProperty("remediations-history-path");
        if (remediationsHistoryPath == null || remediationsHistoryPath.isEmpty()) {
            Logger.getAnonymousLogger().log(Level.WARNING, "The remediations-history-path has not been set" +
                    ", the remediation history will not be kept.");
            return 0;
        }
        double habitIndex = 0;

        //Load the remediations history
        File remediationsHistoryFile = new File(remediationsHistoryPath);
        List<AbstractMap.SimpleEntry<SerializableAttackPath, SerializableDeployableRemediation>> attackPathsRemediations;
        if (remediationsHistoryFile.exists()) {
            ObjectInputStream ois;
            try {
                ois = new ObjectInputStream(new FileInputStream(remediationsHistoryPath));
                attackPathsRemediations = (List<AbstractMap.SimpleEntry<SerializableAttackPath, SerializableDeployableRemediation>>) ois.readObject();
                for (AbstractMap.SimpleEntry<SerializableAttackPath, SerializableDeployableRemediation> attackPathRemediation : attackPathsRemediations) {
                    SerializableAttackPath serializableAttackPath = attackPathRemediation.getKey();
                    SerializableDeployableRemediation serializableRemediation = attackPathRemediation.getValue();
                    if (serializableAttackPath.isSimilarTo(new SerializableAttackPath(correctedPath, informationSystem)) && serializableRemediation.isSimilarTo(this))
                        habitIndex++;
                }
                return habitIndex;
            } catch (Exception e) {
                Logger.getAnonymousLogger().log(Level.WARNING, "Error while loading the remediation history: " + e.getMessage());
            }
        } else {
            return 0;
        }
        return 0;
    }

    /**
     * The list of deployable actions (one action on one machine)
     *
     * @return the actions
     */
    public List<DeployableRemediationAction> getActions() {
        return actions;
    }

    /**
     * Sets actions.
     *
     * @param actions the actions
     */
    public void setActions(List<DeployableRemediationAction> actions) {
        this.actions = actions;
    }

    /**
     * The cost of deploying all these actions
     *
     * @return the cost
     */
    public double getCost() {
        return cost;
    }

    /**
     * Sets cost.
     *
     * @param cost the cost
     */
    public void setCost(double cost) {
        this.cost = cost;
    }

    /**
     * Validate that this remediation has been applied to be taken into account
     * in the remediation automation.
     */
    public void validate(InformationSystem informationSystem) throws Exception {
        Logger.getAnonymousLogger().log(Level.INFO, this.actions.toString() + " has been validated to correct the path.");
        String remediationsHistoryPath = ProjectProperties.getProperty("remediations-history-path");
        if (remediationsHistoryPath == null || remediationsHistoryPath.isEmpty()) {
            Logger.getAnonymousLogger().log(Level.WARNING, "The remediations-history-path has not been set" +
                    ", the remediation history will not be kept.");
            return;
        }

        //Load the remediations history
        File remediationsHistoryFile = new File(remediationsHistoryPath);
        List<AbstractMap.SimpleEntry<SerializableAttackPath, SerializableDeployableRemediation>> attackPathsRemediations;
        if (remediationsHistoryFile.exists()) {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(remediationsHistoryPath));
            attackPathsRemediations = (List<AbstractMap.SimpleEntry<SerializableAttackPath, SerializableDeployableRemediation>>) ois.readObject();
        } else {
            attackPathsRemediations = new ArrayList<AbstractMap.SimpleEntry<SerializableAttackPath, SerializableDeployableRemediation>>();
        }
        Logger.getAnonymousLogger().log(Level.INFO, attackPathsRemediations.size() + " remediated paths in the history file.");

        //Add the current remediation + attack path to the history
        SerializableAttackPath serializableAttackPath = new SerializableAttackPath(this.correctedPath, informationSystem);
        SerializableDeployableRemediation serializableRemediation = new SerializableDeployableRemediation(this);
        attackPathsRemediations.add(new AbstractMap.SimpleEntry<SerializableAttackPath, SerializableDeployableRemediation>(serializableAttackPath, serializableRemediation));

        //Save to the remediations history file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(remediationsHistoryFile));
        oos.writeObject(attackPathsRemediations);
    }
}
import org.apache.commons.io.IOUtils;
import org.fiware.cybercaptor.server.api.AttackPathManagement;
import org.fiware.cybercaptor.server.api.IDMEFManagement;
import org.fiware.cybercaptor.server.api.InformationSystemManagement;
import org.fiware.cybercaptor.server.attackgraph.AttackGraph;
import org.fiware.cybercaptor.server.attackgraph.AttackPath;
import org.fiware.cybercaptor.server.attackgraph.MulvalAttackGraph;
import org.fiware.cybercaptor.server.attackgraph.Vertex;
import org.fiware.cybercaptor.server.database.Database;
import org.fiware.cybercaptor.server.informationsystem.InformationSystem;
import org.fiware.cybercaptor.server.monitoring.Monitoring;
import org.fiware.cybercaptor.server.properties.ProjectProperties;
import org.fiware.cybercaptor.server.remediation.DeployableRemediation;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.json.JSONObject;
import org.json.XML;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/****************************************************************************************
 * This file is part of FIWARE CyberCAPTOR,                                             *
 * instance of FIWARE Cyber Security Generic Enabler                                    *
 * Copyright (C) 2012-2015  Thales Services S.A.S.,                                     *
 * 20-22 rue Grande Dame Rose 78140 VELIZY-VILACOUBLAY FRANCE                           *
 *                                                                                      *
 * FIWARE CyberCAPTOR is free software; you can redistribute                            *
 * it and/or modify it under the terms of the GNU General Public License                *
 * as published by the Free Software Foundation; either version 3 of the License,       *
 * or (at your option) any later version.                                               *
 *                                                                                      *
 * FIWARE CyberCAPTOR is distributed in the hope                                        *
 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied           *
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
 * GNU General Public License for more details.                                         *
 *                                                                                      *
 * You should have received a copy of the GNU General Public License                    *
 * along with FIWARE CyberCAPTOR.                                                       *
 * If not, see <http://www.gnu.org/licenses/>.                                          *
 ****************************************************************************************/
package org.fiware.cybercaptor.server.rest;



/**
 * JSON Rest API, main API, since the XML API has been depreciated.
 *
 * @author Francois -Xavier Aguessy
 */
@Path("/json/")
public class RestJsonAPI {

    /**
     * Generates the attack graph and initializes the main objects for other API calls
     * (database, attack graph, attack paths,...)
     *
     * @param request the HTTP request
     * @return the HTTP response
     * @throws Exception
     */
    @GET
    @Path("initialize")
    @Produces(MediaType.APPLICATION_JSON)
    public Response initialise(@Context HttpServletRequest request) throws Exception {
        String costParametersFolderPath = ProjectProperties.getProperty("cost-parameters-path");
        String databasePath = ProjectProperties.getProperty("database-path");

        //Load the vulnerability and remediation database
        Database database = new Database(databasePath);

        String topologyFilePath = ProjectProperties.getProperty("topology-path");

        Logger.getAnonymousLogger().log(Level.INFO, "Generating topology and mulval inputs " + topologyFilePath);
        InformationSystemManagement.prepareMulVALInputs();

        Logger.getAnonymousLogger().log(Level.INFO, "Loading topology " + topologyFilePath);
        InformationSystem informationSystem = InformationSystemManagement.loadTopologyXMLFile(topologyFilePath, database);


        AttackGraph attackGraph = InformationSystemManagement.generateAttackGraphWithMulValUsingAlreadyGeneratedMulVALInputFile();
        if (attackGraph == null)
            return RestApplication.returnErrorMessage(request, "the attack graph is empty");
        Logger.getAnonymousLogger().log(Level.INFO, "Launch scoring function");
        attackGraph.loadMetricsFromTopology(informationSystem);
        List<AttackPath> attackPaths = AttackPathManagement.scoreAttackPaths(attackGraph, attackGraph.getNumberOfVertices());

        //Delete attack paths that have less than 3 hosts (attacker that pown its own host).
        List<AttackPath> attackPathToKeep = new ArrayList<AttackPath>();
        for (AttackPath attackPath : attackPaths) {
            if (attackPath.vertices.size() > 3) {
                attackPathToKeep.add(attackPath);
            }
        }
        attackPaths = attackPathToKeep;

        Logger.getAnonymousLogger().log(Level.INFO, attackPaths.size() + " attack paths scored");
        Monitoring monitoring = new Monitoring(costParametersFolderPath);
        monitoring.setAttackPathList(attackPaths);
        monitoring.setInformationSystem(informationSystem);
        monitoring.setAttackGraph((MulvalAttackGraph) attackGraph);