@@ -176,9 +249,9 @@
-
+
Commit Object to DB
-
+
Save Object as JSON...
@@ -217,21 +290,22 @@
+
+
+
+
-
-
+
-
+
diff --git a/src/index.ts b/src/index.ts
index 13080731..7d44908b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,128 +1,5 @@
-/*
-Copyright 2018 Southern California Edison Company
+import {main} from './main';
+require('webpack-jquery-ui')
+require('webpack-jquery-ui/controlgroup');
-ALL RIGHTS RESERVED
- */
-
-import { app, BrowserWindow, ipcMain, Event, BrowserWindowConstructorOptions } from 'electron';
-import * as unhandled from 'electron-unhandled';
-import * as path from 'path';
-import * as config_menu from './ui/main-window-menu';
-import * as window_manager from './ui/window-manager';
-import { DatabaseConfigurationStorage } from './storage';
-import { openDatabaseConfigurationDialogMain } from './ui/configuration-dialog-main';
-import { IDatabaseConfigOptions } from './storage/database-configuration-storage';
-
-// tslint:disable-next-line:no-var-requires
-if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
- app.quit();
-}
-
-config_menu.setup();
-
-// Keep a global reference of the window object, if you don't, the window will
-// be closed automatically when the JavaScript object is garbage collected.
-let mainWindow: BrowserWindow;
-
-function createWindow() {
- const window_options: BrowserWindowConstructorOptions = {
- width: 1024,
- // width: winmgr.width,
- height: 768,
- // height: winmgr.height,
- // x: winmgr.x > 0 ? winmgr.x : undefined,
- // y: winmgr.y > 0 ? winmgr.x : undefined,
- icon: path.join(__dirname, '../assets/icons/png/64x64.png'),
- };
- const winmgr = window_manager.instance;
- if (!winmgr.is_initial()) {
- window_options.x = winmgr.x;
- window_options.y = winmgr.y;
- window_options.width = winmgr.width;
- window_options.height = winmgr.height;
- window_options.fullscreen = winmgr.isFullScreen;
- // window_options.webPreferences.nodeIntegration = false;
- }
- mainWindow = new BrowserWindow(window_options);
- if (winmgr.maximize) {
- mainWindow.maximize();
- }
- winmgr.window = mainWindow;
- window_manager.set_events();
-
- mainWindow.loadURL(`file://${__dirname}/index.html`);
-
- ipcMain.on("openDialog", (event: Event) => {
- return event.returnValue = JSON.stringify(DatabaseConfigurationStorage.Instance.currentConfig());
- });
-
- ipcMain.on("closeDialog", (data: IDatabaseConfigOptions) => {
- DatabaseConfigurationStorage.Instance.save(data);
- });
-
- ipcMain.on("prompt", () => {
- openDatabaseConfigurationDialogMain(mainWindow);
- });
-
- ipcMain.on("useDatabase", (_event: Electron.Event, options: IDatabaseConfigOptions) => {
- mainWindow.webContents.send('database_reconfigured', options);
- });
-
- // mainWindow.webContents.openDevTools();
-
- // Emitted when the window is closed.
- mainWindow.on('closed', () => {
- // Dereference the window object, usually you would store windows
- // in an array if your app supports multi windows, this is the time
- // when you should delete the corresponding element.
- window_manager.remove_events();
- mainWindow = null;
- });
-
- // mainWindow.on('unresponsive', () => {
- // const options = {
- // type: 'info',
- // title: 'Main Process Hanging',
- // message: 'This process is hanging.',
- // buttons: ['Reload', "Close"],
- // };
- // dialog.showMessageBox(options, (index) => {
- // if (index === 0) { mainWindow.reload(); } else { mainWindow.close(); }
- // });
- // });
-
-// mainWindow.webContents.on('crashed', () => {
-// const options = {
-// type: 'info',
-// title: 'Renderer Process Crashed',
-// message: 'This process has crashed.',
-// buttons: ['Reload', 'Close'],
-// };
-// dialog.showMessageBox(options, (index) => {
-// if (index === 0) { mainWindow.reload(); } else { mainWindow.close(); }
-// });
-// });
-}
-
-// This method will be called when Electron has finished
-// initialization and is ready to create browser windows.
-// Some APIs can only be used after this event occurs.
-app.on('ready', createWindow);
-
-// Quit when all windows are closed.
-app.on('window-all-closed', () => {
- // On OS X it is common for applications and their menu bar
- // to stay active until the user quits explicitly with Cmd + Q
- if (process.platform !== 'darwin') {
- app.quit();
- }
-});
-
-app.on('activate', () => {
- // On OS X it's common to re-create a window in the app when the
- // dock icon is clicked and there are no other windows open.
- unhandled();
- if (mainWindow === null) {
- createWindow();
- }
-});
+main.prototype.run();
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 4717b8bf..5a393fbc 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,127 +1,470 @@
-/*
-Copyright 2018 Southern California Edison Company
+// /*
+// Copyright 2018 Southern California Edison Company
-ALL RIGHTS RESERVED
- */
+// ALL RIGHTS RESERVED
+// */
-import * as moment from 'moment';
-import * as unhandled from 'electron-unhandled';
-import * as uuid from 'uuid';
+import uuid from 'uuid';
import {
- BundleType,
Relationship,
- SDO,
- SRO,
StixObject,
Indicator,
ObservedData,
Report,
StixNode,
+ StixRelationship,
+ BundleType,
+ SRO,
+ SDO,
} from './stix';
import * as stix from './stix';
import * as fileSaver from 'file-saver';
-// import { ViewUtilitiesOptions } from './cytoscape-view-utilities';
-import { StigDB } from './db/db';
import {
+ compound_style,
edge_style,
node_style,
select_node_style,
view_utils_options,
modified_unselect_style,
modified_select_style,
+ layouts
} from './graph/graphOptions';
-import * as cola from 'cytoscape-cola';
-import * as cosebilkent from 'cytoscape-cose-bilkent';
-import * as dagre from 'cytoscape-dagre';
-import * as euler from 'cytoscape-euler';
-import * as ngraph from 'cytoscape-ngraph.forcelayout';
-import * as spread from 'cytoscape-spread';
-import { QueryHistoryDialog } from './ui/queryHistoryWidget';
+import cola from 'cytoscape-cola';
+import cosebilkent from 'cytoscape-cose-bilkent';
+import dagre from 'cytoscape-dagre';
+import euler from 'cytoscape-euler';
+// import ngraph from 'cytoscape-ngraph.forcelayout';
+import spread from 'cytoscape-spread';
import { setup_edge_handles, edgehandles_style } from './graph/edge-handles';
import { setup_ctx_menu } from './graph/context-menu';
import { GraphUtils } from './graph/graphFunctions';
import { StixEditor } from './ui/stix-editor';
-import * as Split from 'split.js';
-import * as cytoscape from 'cytoscape';
+import Split from 'split.js';
+import cytoscape, { CytoscapeOptions } from 'cytoscape';
import { ViewUtilitiesOptions } from './graph/graphOptions';
-import { ipcRenderer } from 'electron';
-import { GraphQueryResult } from './db/db_types';
-import * as edgehandles from 'cytoscape-edgehandles';
+import edgehandles from 'cytoscape-edgehandles';
+import moment from 'moment';
import { QueryStorageService, DatabaseConfigurationStorage, StigSettings } from './storage';
-import { setHandlers } from './ui/ipc-render-handlers';
+import {graph_copy, graph_paste} from './ui/clipboard'
+import { openDatabaseConfiguration } from './ui/database-config-widget';
+import { check_db, commit, get_diff, query } from './db/dbFunctions';
+import { commit_all, delete_selected } from './ui/ipc-render-handlers';
+import { GraphQueryResult } from './db/db_types';
+import { QueryHistoryDialog } from './ui/queryHistoryWidget';
+import { DiffDialog } from './ui/diff-dialog';
+import { removeCompoundNodes, initDefenseGraph, initKillChainGraph } from './contextLayouts/contextLayouts';
+import tippy from 'tippy.js'
+import { organizeOrphans } from './contextLayouts/graphLayoutFunctions';
+
+const killChain = require("./contextLayouts/killChainSchema.json")
+const defense = require("./contextLayouts/defenseInDepthSchema.json")
+import { openDatabaseUpload } from './ui/database-upload-widget';
+import { openConnectTaxii } from './ui/connect-taxii-widget';
+import { openBundleExport } from './ui/export -bundle-widget';
declare global {
- interface Window {
+ interface Window {
cycore: cytoscape.Core;
layout: string;
}
}
+// Returns nodes positions
+const getNodeMetadata = (nodes) => {
+ return nodes.map(obj => ({
+ id: obj.id(),
+ position: obj.position(),
+ }))
+}
+
// tslint:disable-next-line:class-name
export class main {
// tslint:disable-next-line:no-empty
constructor() { }
- public run() {
+ public async run() {
+
+ // Initialize tippy tooltips
+ tippy('[data-tippy-content]', {
+ theme: 'light',
+ });
+ // Initialize the query storage object
const storage: QueryStorageService = QueryStorageService.Instance;
+
+ // Initialize the settings object
+ const settings = StigSettings.Instance;
+
let loading: boolean = false;
document.addEventListener('DOMContentLoaded', async () => {
- unhandled();
- const cyto_options: cytoscape.CytoscapeOptions = {
+
+ // load saved data
+ await DatabaseConfigurationStorage.Instance.getConfigs()
+ await StigSettings.Instance.getSettings()
+ await QueryStorageService.Instance.getQueryHistory()
+
+ const cyto_options: CytoscapeOptions = {
container: $('#cy')[0],
- style: [node_style, edge_style, select_node_style, modified_select_style, modified_unselect_style, ...edgehandles_style],
- // wheelSensitivity: 0.25,
+ style: [node_style, compound_style, edge_style, select_node_style, modified_select_style, modified_unselect_style, ...edgehandles_style],
+ wheelSensitivity: 0.25,
} as cytoscape.CytoscapeOptions;
// set up cytoscape
const cy = cytoscape(cyto_options);
window.cycore = cy;
+ const graph_utils = new GraphUtils(cy);
+
// used by some events to make cytoscape respond
- window.addEventListener("resize", () => cy.resize(), false);
+ window.addEventListener("resize", () => {cy.resize()}, false);
const call_forceRender = () => {
cy.resize();
};
- Split(['#cy', '#editpane'], {
+ var split = Split(['#cy', '#editpane'], {
direction: 'horizontal',
sizes: [75, 25],
gutterSize: 8,
cursor: 'col-resize',
onDragEnd: call_forceRender,
});
- const current_db_config = DatabaseConfigurationStorage.Instance.current;
- const db = new StigDB(current_db_config);
+
+
+
+ // Add event listeners to dropdown menu items
+
+ // GRAPH
+ $("#dd-copyElem").on("click", () => {
+ //console.log("Copy element")
+ graph_copy();
+ })
+ $("#dd-cutElem").on("click", () => {
+ //console.log("Cut element")
+ const selected = window.cycore.$(":selected")
+ graph_copy()
+ window.cycore.remove(selected)
+ })
+ $("#dd-pasteElem").on("click", () => {
+ //console.log("paste element")
+ graph_paste()
+ })
+ $("#dd-commitElem").on("click", () => {
+ //console.log("Commit elements")
+ commit_all()
+ })
+ $("#dd-dbDelete").on("click", () => {
+ //console.log("Delete from db")
+ delete_selected()
+ })
+ $("#dd-selectElem").on("click", () => {Blob
+ //console.log("Select all elements")
+ window.cycore.elements().select()
+ })
+ $("#dd-invertSelect").on("click", () => {
+ console.log("Invert")
+ const unselected = window.cycore.$(':unselected');
+ const selected = window.cycore.$(':selected');
+ selected.unselect();
+ unselected.select();
+ })
+ $("#dd-viewEmbeddedRels").on("click", () => {
+ // list all objects with property "object_refs"
+ let embedded_rel_types = ["report", "opinion", "grouping", "note", "observed-data"]
+
+ // get all current objects
+ let nodes = window.cycore.$(':visible')
+ nodes = nodes.union(nodes.connectedEdges());
+ //let new_rels = []
+
+ // if an object has "object_refs" property, create relationships for all reference objects
+ nodes.each((ele) => {
+ console.log(ele)
+ let current_object = ele.data('raw_data');
+ if (current_object !== undefined && embedded_rel_types.includes(current_object['type']) && current_object['object_refs']) {
+ console.log(current_object["object_refs"])
+ console.log(current_object["id"])
+ for (var ref_id of current_object["object_refs"]) {
+ console.log(ref_id)
+
+ // Step 1.
+ let rel_id = uuid.v4();
+ //let rel_created = moment().utc().format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')
+
+ // const raw_data: Relationship = {
+ // // get source node
+ // source_ref: current_object["id"],
+ // // get target node
+ // target_ref: ref_id,
+ // type: 'relationship',
+ // created: rel_created,
+ // id: rel_id,
+ // relationship_type: 'related-to',
+ // };
+
+ const opts: stix.VisualEdgeData = {
+ raw_data: "visual_edge",
+ id: rel_id,
+ target: ref_id,
+ source: current_object["id"]
+ }
+
+ // const opts: stix.StixRelationshipData = {
+ // raw_data: raw_data,
+ // type: 'relationship',
+ // id: rel_id,
+ // created: rel_created,
+ // source: current_object["id"],
+ // target: ref_id,
+ // label: 'related-to'
+ // };
+
+ // console.log(opts)
+
+ // Step 2.
+ let rel_node = new stix.VisualEdge(opts)
+
+ // Step 3.
+ cy.add(rel_node);
+ // cy.add({data: {id: rel_id, source: current_object["id"], target: ref_id}});
+ }
+ }
+ //&& ele.data('raw_data').object_refs
+ });
+ })
+
+ var relationshipsOn = true
+ $("#dd-toggleRelationships").on("click", () => {
+ if (relationshipsOn) {
+ view_util.hide(cy.edges())
+ } else {
+ view_util.show(cy.edges())
+ }
+ relationshipsOn = !relationshipsOn
+ })
+
+ // EXPORT
+ $("#dd-exportSelected").on("click", () => {
+ //console.log("Export selected")
+ const bundle_id = 'bundle--' + uuid.v4();
+ const bundle = { type: 'bundle', id: bundle_id, objects: [] } as BundleType;
+
+ let nodes = window.cycore.$(':selected');
+ nodes.each((ele) => {
+ if (ele.length === 0) {
+ return;
+ }
+ //logic to remove null on json export
+ if(ele.data('raw_data')!==undefined){
+ bundle.objects.push(ele.data('raw_data'));
+ }
+ });
+
+ openBundleExport(bundle)
+ })
+ $("#dd-exportGraph").on("click", () => {
+ //console.log("Export graph")
+ const bundle_id = 'bundle--' + uuid.v4();
+ const bundle = { type: 'bundle', id: bundle_id, objects: [] } as BundleType;
+
+ let nodes = window.cycore.$(':selected');
+ nodes.each((ele) => {
+ if (ele.length === 0) {
+ return;
+ }
+ //logic to remove null on json export
+ if(ele.data('raw_data')!==undefined){
+ bundle.objects.push(ele.data('raw_data'));
+ }
+ });
+
+ openBundleExport(bundle)
+ })
+ $("#dd-exportAll").on("click", () => {
+ //console.log("Export all")
+ const bundle_id = 'bundle--' + uuid.v4();
+ const bundle = { type: 'bundle', id: bundle_id, objects: [] } as BundleType;
+ let nodes = window.cycore.$(':visible');
+ nodes = nodes.union(nodes.connectedEdges());
+ nodes.each((ele) => {
+ if (ele.length === 0) {
+ return;
+ }
+ //logic to remove null on json export
+ if(ele.data('raw_data')!==undefined){
+ bundle.objects.push(ele.data('raw_data'));
+ }
+ });
+ bundle.metadata = getNodeMetadata(nodes);
+
+ openBundleExport(bundle)
+ })
+ $("#dd-exportPos").on("click", () => {
+ //console.log("Export positions")
+ let nodes = window.cycore.$(':visible')
+
+ const jsonObj = JSON.stringify({metadata: getNodeMetadata(nodes)}, null, 2);
+ const blob = new Blob([jsonObj], { type: "application/json" });
+ fileSaver.saveAs(blob, "metadata.json");
+ $('.message-status').html("exported metadata")
+ })
+
+ // LAYOUT
+ $("#dd-layoutCose").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutCose").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("cose");
+ settings.setLayout("cose");
+ })
+ $("#dd-layoutCola").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutCola").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("cola");
+ settings.setLayout("cola");
+ })
+ $("#dd-layoutCircle").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutCircle").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("circle");
+ settings.setLayout("circle");
+ })
+ $("#dd-layoutSpread").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutSpread").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("spread");
+ settings.setLayout("spread");
+ })
+ $("#dd-layoutCoseBilkent").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutCoseBilkent").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("cose_bilkent");
+ settings.setLayout("cose_bilkent");
+ })
+ $("#dd-layoutKlay").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutKlay").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("klay");
+ settings.setLayout("klay");
+ })
+ $("#dd-layoutDagre").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutDagre").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("dagre");
+ settings.setLayout("dagre");
+ })
+ $("#dd-layoutRandom").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutRandom").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("random");
+ settings.setLayout("random");
+ })
+ $("#dd-layoutConcentric").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutConcentric").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("concentric");
+ settings.setLayout("concentric");
+ })
+ $("#dd-layoutBreadthfirst").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutBreadthfirst").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("breadthfirst");
+ settings.setLayout("breadthfirst");
+ })
+ $("#dd-layoutGrid").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-layout")}).prop("style", "background-color: white")
+ $("#dd-layoutGrid").prop("style", "background-color: #0d6efd")
+
+ graph_utils.myLayout("grid");
+ settings.setLayout("grid");
+ })
+
+ // CONTEXT LAYOUTS
+ $("#dd-ctxLayoutNone").prop("style", "background-color: #0d6efd")
+ $("#dd-ctxLayoutNone").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-ctxLayout")}).prop("style", "background-color: white")
+ $("#dd-ctxLayoutNone").prop("style", "background-color: #0d6efd")
+
+ removeCompoundNodes()
+ // Position the nodes with a layout
+ graph_utils.myLayout(StigSettings.Instance.layout.toLowerCase());
+ })
+ $("#dd-ctxLayoutDefInDepth").on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-ctxLayout")}).prop("style", "background-color: white")
+ $("#dd-ctxLayoutDefInDepth").prop("style", "background-color: #0d6efd")
+
+ removeCompoundNodes()
+ initDefenseGraph()
+ organizeOrphans()
+ })
+ killChain["kill-chain"].forEach(kc => {
+ $("#contextLayoutOptions").append(`
${kc.type} `)
+ $(`#ctxLayout${kc.type}`).on("click", () => {
+ $("a").filter(function(_index: number, ele: HTMLElement) {return ele.id.includes("dd-ctxLayout")}).prop("style", "background-color: white")
+ $(`#dd-ctxLayout${kc.type}`).prop("style", "background-color: #0d6efd")
+
+ removeCompoundNodes()
+ initKillChainGraph(kc.type)
+
+ })
+ })
+
+
+ // DATABASE
+ $("#database").on("click", () => {
+ openDatabaseConfiguration();
+ })
+ $("#databaseUpload").on("click", () => {
+ openDatabaseUpload();
+ })
+ $("#connectTaxii").on("click", () => {
+ openConnectTaxii();
+ })
// Graph handling functions
- const graph_utils = new GraphUtils(cy, db);
+
// configures edge behaviors
edgehandles(cytoscape);
- setup_edge_handles(cy);
+ var eh = setup_edge_handles(cy);
+
+ // Disable edgehandles for compound nodes
+ cy.on("mouseover", ":parent", _ => {eh.hide()})
// set up view utilities
const jquery = require('jquery');
- // var jquery;
const viewUtilities = require('cytoscape-view-utilities');
viewUtilities(cytoscape, jquery);
const view_util = cy.viewUtilities(view_utils_options as ViewUtilitiesOptions);
// context menus inside the graph
const cxtmenu = require('cytoscape-cxtmenu');
cxtmenu(cytoscape);
- setup_ctx_menu(cy, db, view_util);
+ setup_ctx_menu(cy, view_util);
const klay = require('cytoscape-klay');
klay(cytoscape);
cola(cytoscape);
cosebilkent(cytoscape);
dagre(cytoscape);
euler(cytoscape);
- ngraph(cytoscape);
+ // ngraph(cytoscape);
spread(cytoscape);
- setHandlers();
+
+ // Get the layout from the cookie and set the graph layout
+ let layout = (await settings.getSettings()).layout
+ //console.log('layout: ', layout)
+ if (layouts[layout]) {
+ graph_utils.myLayout(layout)
+ }
// the editor form that is filled when a node is clicked
- const editor = new StixEditor(cy, db);
+ const editor = new StixEditor(cy);
//#endregion
// function to search elements inside the displayed graph
@@ -239,42 +582,46 @@ export class main {
return;
}
storage.add(text);
- hist_dialog.addToHistoryDialog();
- const result = await db.doGraphQuery({
- command: text,
- mode: 'graph',
- parameters: [],
- });
- if (result.graph === undefined || result.graph.vertices === undefined) {
- $('#query-status').html('No results');
- return;
- }
- // console.log(result);
- $('#query-status').html(`Returned ${result.graph.vertices.length} nodes and ${result.graph.edges.length} edges.`);
+ // hist_dialog.addToHistoryDialog();
+ // const result = await db.doGraphQuery({
+ // command: text,
+ // mode: 'graph',
+ // parameters: [],
+ // });
+ const result = await query(text)
+ // if (result.graph === undefined || result.graph.vertices === undefined) {
+ // $('#query-status').html('No results');
+ // return;
+ // }
+
+ //console.log(result);
+
const add_graph: GraphQueryResult = {
graph: {
vertices: [],
edges: [],
},
};
- let results: StixObject[] = [];
- results = results.concat(result.graph.edges, result.graph.vertices);
+ // let results: StixObject[] = [];
+ // results = results.concat(result.graph.edges, result.graph.vertices);
// results.concat(result.graph.vertices);
loading = true;
- results.forEach((item: StixObject) => {
+ result.forEach((item: StixObject) => {
if (cy.getElementById(item.id_).length === 0) {
/((r|R)elationship)|((s|S)ighting)/.exec(item.type) ? add_graph.graph.edges.push(item as SRO) : add_graph.graph.vertices.push(item as SDO);
}
});
+ $('#query-status').html(`Returned ${add_graph.graph.vertices.length} nodes and ${add_graph.graph.edges.length} edges.`);
try {
- const bundle = await db.handleResponse(add_graph);
- const new_nodes = await graph_utils.buildNodes(bundle, true);
+ // const bundle = await db.handleResponse(add_graph);
+ let bundle : BundleType = {type: "bundle", objects: result}
+ await graph_utils.buildNodes(bundle, true);
// const selected = cy.$(':selected');
// view_util.removeHighlights(selected);
- cy.elements().unselect();
+ // cy.elements().unselect();
graph_utils.myLayout(StigSettings.Instance.layout.toLowerCase());
- new_nodes.select();
- $('.message-status').html(`Added ${new_nodes.length} elements to graph`);
+ // new_nodes.select();
+ $('.message-status').html(`Added ${result.length} elements to graph`);
loading = false;
} catch (err) {
loading = false;
@@ -292,13 +639,14 @@ export class main {
$("#btn-db-search").trigger("click");
}
});
+
// clears all items from displayed graph
$("#button-clear-graph").on("click", (e: JQuery.Event) => {
e.preventDefault();
e.stopPropagation();
cy.elements().remove();
$('#metawidget').empty();
- db.diff_dialog.reset();
+ // db.diff_dialog.reset();
cy.reset();
$("#query-status").html("No Results");
});
@@ -313,18 +661,20 @@ export class main {
const input_data = ele.data('raw_data');
if (input_data === undefined) { return true; }
if (ele.isNode()) {
+ // console.log("
search schema", schema)
// load the form for this node
try {
editor.buildWidget(ele, ele.data('type'), input_data);
}
catch(err) {
- if(err.message === "Cannot read property '$ref' of undefined"){
- // added to handle sub directory 'observables'
- editor.buildWidget(ele, 'observables/' + ele.data('type'), input_data);
- }
- else{
+ // console.log(err.message);
+ // if(err.message === "Cannot read property '$ref' of undefined"){
+ // // added to handle sub directory 'observables'
+ // editor.buildWidget(ele, 'observables/' + ele.data('type'), input_data);
+ // }
+ // else{
console.error(err);
- }
+ // }
}
} else {
// edge
@@ -362,7 +712,7 @@ export class main {
// console.log(editor.root.getValue())
e.preventDefault();
e.stopPropagation();
- const form_data = editor.editor.getEditor('root').getValue();
+ const form_data = editor.editor.getValue();
const jsonToSave = JSON.stringify(form_data, null, 2);
const jsonSingleSave = new Blob([jsonToSave], { type: "application/json" });
fileSaver.saveAs(jsonSingleSave, `${form_data.id}.json`);
@@ -370,7 +720,7 @@ export class main {
});
// Clear Stix form editor when node/edge is unselected
- cy.on("unselect", 'node, edge', (_evt: cytoscape.EventObject) => {
+ cy.on("unselect", 'node, edge', (evt: cytoscape.EventObject) => {
// editor.editor.destroy();
$('#metawidget').empty();
$('#current_node').empty();
@@ -380,7 +730,10 @@ export class main {
// Handler for when an edge is created via the graph editor
cy.on('add', 'edge', (evt: cytoscape.EventObject) => {
+ console.log("in here")
+
let my_map = new Map();
+
my_map.set('attack-pattern', 'uses');
my_map.set('campaign', 'uses');
my_map.set('course-of-action', 'mitigates');
@@ -402,12 +755,13 @@ export class main {
const input_data = ele.data('raw_data');
if (input_data === undefined) {
-
+ console.log("in here as well")
let src_obj_type = ele.source().data('raw_data').type;
let default_relationship = "";
if (my_map.has(src_obj_type)) {
default_relationship = my_map.get(src_obj_type);
- } else {
+ }
+ else {
default_relationship = "related-to";
}
@@ -428,20 +782,6 @@ export class main {
}
});
- ipcRenderer.on("layout", (_event: Electron.Event, layout_type: string) => {
- // layout = layout_type;
- // window.layout = layout_type;
- graph_utils.myLayout(layout_type);
- });
-
- ipcRenderer.on('database_reconfigured', () => {
- cy.elements().remove();
- $('#metawidget').empty();
- db.diff_dialog.reset();
- cy.reset();
- $("#query-status").html("No Results");
- });
-
// Handlers for drag and drop of files containing stix bundles
const uploader: HTMLElement = document.getElementById('cy')!;
@@ -501,8 +841,39 @@ export class main {
function addToGraph(pkg: BundleType) {
graph_utils.buildNodes(pkg, false).then((added) => {
$('.message-status').html(`Added ${added.length} elements to graph.`);
+ if (pkg.metadata) {
+ // Position the nodes
+ for (const node of pkg.metadata) {
+ // Find the element on the graph
+ cy.$id(node.id).animate({position: node.position, duration: 1000, complete: () => cy.fit()})
+ }
+ } else {
+
+ var canLayout = true
+
+ // Check if defense in depth is on
+ if (cy.nodes(`#${defense.name.replaceAll(" ", "_")}`).length > 0) {
+ canLayout = false
+ $("#dd-ctxLayoutDefInDepth").trigger("click")
+ }
+
+ // Check if a kill chain is on
+ killChain["kill-chain"].forEach(kc => {
+ // `#ctxLayout${kc.type}`
+ if (cy.nodes(`#${kc.type}`).length > 0) {
+ canLayout = false
+ $(`#ctxLayout${kc.type}`).trigger("click")
+ }
+ })
+
+ // Only do this if there aren't any defense in depth or kill chain layouts open
+ if (canLayout) {
+ graph_utils.myLayout(StigSettings.Instance.layout.toLowerCase());
+ }
+ }
+
+
});
- graph_utils.myLayout(StigSettings.Instance.layout.toLowerCase());
}
uploader.addEventListener('dragover', handleDragOver, false);
uploader.addEventListener('drop', handleFileDrop, false);
@@ -512,11 +883,12 @@ export class main {
*
*/
$(document).on('click', '#btn-export-bundle', () => {
+
// Get raw data from all cy elements
// Create bundle object
const bundle_id = 'bundle--' + uuid.v4();
const bundle = { type: 'bundle', id: bundle_id, objects: [] } as BundleType;
- let nodes = cy.$(':visible');
+ let nodes = window.cycore.$(':visible');
nodes = nodes.union(nodes.connectedEdges());
nodes.each((ele) => {
if (ele.length === 0) {
@@ -533,39 +905,54 @@ export class main {
// bundle.objects.push(ele.data('raw_data'));
// });
- // Convert to JSON and save
- const jsonToSave = JSON.stringify(bundle, null, 2);
- const jsonBundleSave = new Blob([jsonToSave], { type: "application/json" });
- fileSaver.saveAs(jsonBundleSave, "bundle.json");
- $('.message-status').html(`Exported ${bundle.objects.length} objects`);
+ // Open the export widget
+ openBundleExport(bundle)
});
- $(document).on('click', '#btn-diff', () => {
- db.diff_dialog.open();
+ $(document).on('click', '#btn-diff', async () => {
+ const node = editor.editor.getValue()
+ const diff = await get_diff(node)
+ if (diff) {
+ let diff_dialog = new DiffDialog($('#diff-anchor'))
+ diff_dialog.addDiff(node.id, diff, node, node.name)
+ diff_dialog.open();
+ } else {
+ console.log("Error: no diff")
+ }
});
/***************************************** *
* Save stix form to DB on click
*************************************** */
$('button').button();
- $('button.btn-commit').on("click", (e: JQuery.Event) => {
+ $('button.btn-commit').on("click", async (e: JQuery.Event) => {
e.preventDefault();
e.stopPropagation();
- let result: StixObject[];
- let ourres = '';
+ // let result: StixObject[];
+ // let ourres = '';
try {
const formdata: StixObject = editor.editor.getValue();
- db.updateDB(formdata).then((r) => {
- result = r;
- // ourres = result[0]['type'];
- });
+ if (await commit(formdata)) {
+ // Find the node in the graph
+ let node = cy.elements().filter((ele) => {
+ //console.log(JSON.stringify(ele.data('saved')))
+ return ele?.data('id') === formdata.id;
+ })
+
+ // Set it as saved
+ if (node[0]) {
+ node[0].data('saved', true)
+ }
+
+ $('.message-status').html(`Committed 1 object to the database.`);
+
+ }
} catch (e) {
console.error('Error saving to database:');
console.error(e);
throw e;
}
$('button.btn-commit').button('option', 'disabled', true);
- $('.message-status').html(`Committed 1 object to the database.`);
});
/***********************************************************************************
@@ -647,14 +1034,14 @@ export class main {
opacity: 0.7,
helper: 'clone',
zIndex: 999,
- start() {
- // console.log('icon position:' + $(element).attr('position').x + $(element).attr('position').y)
- // console.log('drag start:' + ui.position.left.toString() + ' ' + ui.position.top.toString())
- },
+ // start(_, ui) {
+ // // console.log('icon position:' + $(element).attr('position').x + $(element).attr('position').y)
+ // console.log('drag start:' + ui.position.left.toString() + ' ' + ui.position.top.toString())
+ // },
// handle widgets being dragged in from the widget bar
- stop: async (evt: DragEvent) => {
+ stop: async function(_, ui) {
const my_node = await event_add_node($(element).attr('name')!);
- const view_pos = cy._private.renderer.projectIntoViewport(evt.clientX, evt.clientY);
+ const view_pos = cy._private.renderer.projectIntoViewport(ui.position.left, ui.position.top);
my_node.position = {
x: view_pos[0],
y: view_pos[1],
@@ -664,9 +1051,49 @@ export class main {
});
return;
});
+
+ // // Save graph if the page refreshes
+ // // Note: Doesn't work on large graphs. Cookies have limited size.
+ // $(window).on('beforeunload', async () => {
+ // let graph = []
+ // window.cycore.elements().each(ele => {
+ // if (ele.data('raw_data')) {
+ // graph.push(ele.data('raw_data'))
+ // }
+ // })
+
+ // document.cookie = `bundle=${JSON.stringify({type: 'bundle', objects: graph})}; sameSite=strict`
+ // // fetch('/save', {
+ // // method: 'POST',
+ // // headers: {
+ // // 'Content-Type': 'application/json'
+ // // },
+ // // body: JSON.stringify({name: 'graph', data: {type: 'bundle', objects: graph}})
+ // // })
+ // })
+
+ // // Check if there is a saved graph in the cookies
+ // if (document.cookie.includes('bundle')) {
+ // //let bundle = JSON.stringify(document.cookie)
+ // let cookie = document.cookie;
+ // let cookiePieces = cookie.split(';')
+ // let bundleCookie = cookiePieces.find(piece => {return piece.includes('bundle=')})
+ // console.log(bundleCookie)
+ // let bundle = bundleCookie.split('=');
+ // console.log(bundle[1])
+ // let parsed = JSON.parse(bundle[1])
+ // const test_stix = (i: any) => {
+ // const ret = i instanceof Object && i.hasOwnProperty('type') && i.hasOwnProperty('created');
+ // return ret;
+ // };
+ // parsed.objects.every(test_stix) ? parsed.objects : parsed.objects = [];
+
+ // graph_utils.buildNodes(parsed).then(() => graph_utils.myLayout(settings.layout))
+
+ // }
},
);
- //
+
/*document.addEventListener("DOMContentLoaded", function() {
var cy = (window.cy = cytoscape({
container: document.getElementsByClassName("icon-box"),
@@ -708,5 +1135,7 @@ export class main {
});
//*/
+
+ setInterval(check_db, 10000)
}
}
diff --git a/src/static/index.css b/src/static/index.css
index e3e287d5..02800da0 100644
--- a/src/static/index.css
+++ b/src/static/index.css
@@ -73,6 +73,8 @@ div#cy {
/*border: 1px solid gray;*/
}
+
+
img.loadlater {
box-sizing: border-box;
height: auto;
@@ -169,6 +171,12 @@ div.btn-history {
z-index: 1000;
}
+#top-menu {
+ cursor: default;
+ background-color: #6c757d;
+ width: 100%;
+}
+
/*============================================
= Main Layout Grid starts here =
=============================================*/
@@ -404,3 +412,25 @@ div.btn-history {
/*===== End of Messages Row ======*/
/*===== End of Main Grid ======*/
+
+.db-status-red {
+ background-color: #ff7a70;
+ border-radius: 5px;
+ border: 1px solid #ff7a70;
+}
+
+.db-status-green {
+ background-color: #50a03c;
+ border-radius: 5px;
+ border: 1px solid #50a03c;
+}
+
+/*==== Context Layout Nodes ====*/
+.context-node {
+ content: 'data(id)';
+ text-align: 'center';
+}
+
+.layer {
+ background-color: #2196F3
+}
\ No newline at end of file
diff --git a/src/static/jsedit/attack-pattern-relationship.json b/src/static/jsedit/attack-pattern-relationship.json
index 7a327f00..a03a96ed 100644
--- a/src/static/jsedit/attack-pattern-relationship.json
+++ b/src/static/jsedit/attack-pattern-relationship.json
@@ -34,8 +34,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/campaign-relationship.json b/src/static/jsedit/campaign-relationship.json
index 98fc6018..5d46e189 100644
--- a/src/static/jsedit/campaign-relationship.json
+++ b/src/static/jsedit/campaign-relationship.json
@@ -36,8 +36,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/common-relationship.json b/src/static/jsedit/common-relationship.json
index 89f3243b..84ad7a43 100644
--- a/src/static/jsedit/common-relationship.json
+++ b/src/static/jsedit/common-relationship.json
@@ -31,8 +31,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/course-of-action-relationship.json b/src/static/jsedit/course-of-action-relationship.json
index ba7311e7..4d594c5b 100644
--- a/src/static/jsedit/course-of-action-relationship.json
+++ b/src/static/jsedit/course-of-action-relationship.json
@@ -35,8 +35,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/extension-definition.json b/src/static/jsedit/extension-definition.json
new file mode 100644
index 00000000..36aa5c43
--- /dev/null
+++ b/src/static/jsedit/extension-definition.json
@@ -0,0 +1,73 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "extension-definition",
+ "description": "The extension-definition object represents a specific extension.",
+ "type": "object",
+ "$ref": "static/jsedit/core.json",
+ "properties": {
+ "id": {
+ "title": "id",
+ "pattern": "^extension-definition--",
+ "default": "extension-definition--",
+ "type": "string",
+ "readonly": "true"
+ },
+ "type": {
+ "type": "string",
+ "description": "The type of this object, which MUST be the literal `extension-definition`.",
+ "default": "extension-definition",
+ "readonly": "true"
+ },
+ "name" : {
+ "type" : "string",
+ "description": "A name used for display purposes during execution, development, or debugging."
+ },
+ "description": {
+ "type": "string",
+ "format": "textarea",
+ "description": "A detailed explanation of what data the extension conveys and how it is intended to be used."
+ },
+ "schema" : {
+ "type" : "string",
+ "description": "The normative definition of the extension, either as a URL or as plain text explaining the definition. A URL SHOULD point to a JSON schema or a location that contains information about the schema."
+ },
+ "version" : {
+ "type" : "string",
+ "description": "The version of this extension. Producers of STIX extensions are encouraged to follow standard semantic versioning procedures where the version number follows the pattern, MAJOR.MINOR.PATCH. This will allow consumers to distinguish between the three different levels of compatibility typically identified by such versioning strings."
+ },
+ "extension_types": {
+ "type": "array",
+ "description": "This property specifies one or more extension types contained within this extension.",
+ "items": {
+ "type": "string",
+ "$ref": "#/definitions/extension-type-enum"
+ },
+ "minItems": 1
+ },
+ "extension_properties": {
+ "type": "array",
+ "description": "This property contains the list of new property names that are added to an object by an extension.",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1
+ }
+ },
+ "required": [
+ "schema",
+ "version",
+ "extension_types"
+ ],
+ "definitions": {
+ "extension-type-enum": {
+ "type": "string",
+ "enum": [
+ "new-sdo",
+ "new-sco",
+ "new-sro",
+ "property-extension",
+ "toplevel-property-extension"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/static/jsedit/identity-relationship.json b/src/static/jsedit/identity-relationship.json
index 49a0fc89..c7e8a2f7 100644
--- a/src/static/jsedit/identity-relationship.json
+++ b/src/static/jsedit/identity-relationship.json
@@ -32,8 +32,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/indicator-relationship.json b/src/static/jsedit/indicator-relationship.json
index b3fe4ea2..fa8f8c50 100644
--- a/src/static/jsedit/indicator-relationship.json
+++ b/src/static/jsedit/indicator-relationship.json
@@ -33,8 +33,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/indicator.json b/src/static/jsedit/indicator.json
index e966bcab..e671d1bc 100644
--- a/src/static/jsedit/indicator.json
+++ b/src/static/jsedit/indicator.json
@@ -24,6 +24,7 @@
},
"description": {
"type": "string",
+ "format": "textarea",
"description": "A description that provides the recipient with context about this Indicator potentially including its purpose and its key characteristics.",
"format": "textarea"
},
@@ -70,8 +71,7 @@
"required": [
"pattern",
"pattern_type",
- "valid_from",
- "indicator_types"
+ "valid_from"
],
"definitions": {
"indicator-type-ov": {
diff --git a/src/static/jsedit/infrastructure-relationship.json b/src/static/jsedit/infrastructure-relationship.json
index 228e71a5..0f745b13 100644
--- a/src/static/jsedit/infrastructure-relationship.json
+++ b/src/static/jsedit/infrastructure-relationship.json
@@ -29,6 +29,7 @@
"delivers",
"hosts",
"has",
+ "located-at",
"uses",
"related-to",
"created-by",
@@ -38,8 +39,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/infrastructure.json b/src/static/jsedit/infrastructure.json
index 70ba8c09..fb447f63 100644
--- a/src/static/jsedit/infrastructure.json
+++ b/src/static/jsedit/infrastructure.json
@@ -60,8 +60,7 @@
}
},
"required": [
- "id",
- "infrastructure_types"
+ "id"
],
"definitions": {
"infrastructure-type-ov": {
@@ -76,12 +75,12 @@
"firewall",
"hosting-malware",
"hosting-target-lists",
- "infrastructure",
"phishing",
"reconnaissance",
+ "routers-switches",
"staging",
- "unknown",
- "workstation"
+ "workstation",
+ "unknown"
]
}
}
diff --git a/src/static/jsedit/intrusion-set-relationship.json b/src/static/jsedit/intrusion-set-relationship.json
index 4140864f..19d0d236 100644
--- a/src/static/jsedit/intrusion-set-relationship.json
+++ b/src/static/jsedit/intrusion-set-relationship.json
@@ -37,8 +37,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/location-relationship.json b/src/static/jsedit/location-relationship.json
index 89f3243b..84ad7a43 100644
--- a/src/static/jsedit/location-relationship.json
+++ b/src/static/jsedit/location-relationship.json
@@ -31,8 +31,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/malware-analysis-relationship.json b/src/static/jsedit/malware-analysis-relationship.json
index 8d63c7dd..ac1f0918 100644
--- a/src/static/jsedit/malware-analysis-relationship.json
+++ b/src/static/jsedit/malware-analysis-relationship.json
@@ -35,8 +35,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/malware-relationship.json b/src/static/jsedit/malware-relationship.json
index 2c7590ac..e7862ecc 100644
--- a/src/static/jsedit/malware-relationship.json
+++ b/src/static/jsedit/malware-relationship.json
@@ -43,8 +43,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/malware.json b/src/static/jsedit/malware.json
index 51f49780..6801e436 100644
--- a/src/static/jsedit/malware.json
+++ b/src/static/jsedit/malware.json
@@ -40,8 +40,8 @@
"type": "boolean",
"description": "Whether the object represents a malware family (if true) or a malware instance (if false).",
"enum": [
- true,
- false
+ false,
+ true
]
},
"aliases": {
@@ -108,8 +108,7 @@
}
},
"required": [
- "is_family",
- "malware_types"
+ "is_family"
],
"definitions": {
"malware-type-ov": {
diff --git a/src/static/jsedit/marking-definition.json b/src/static/jsedit/marking-definition.json
index a97f7785..db8aded4 100644
--- a/src/static/jsedit/marking-definition.json
+++ b/src/static/jsedit/marking-definition.json
@@ -21,6 +21,26 @@
"type" : "string",
"description": "A name used to identify the Marking Definition."
},
+ "definition_type": {
+ "type": "string",
+ "description": "The definition_type property identifies the type of Marking Definition.",
+ "$ref": "#/definitions/marking-definition-type"
+ },
+ "definition": {
+ "type": "object",
+ "description": "The definition property contains the marking object itself.",
+ "properties": {
+ "statement": {
+ "type": "string",
+ "defintion": "A Statement (e.g., copyright, terms of use) applied to the content marked by this marking definition."
+ },
+ "tlp": {
+ "type": "string",
+ "defintion": "The TLP level of the content marked by this marking definition, as defined in this section.",
+ "$ref": "#/definitions/tlp-level"
+ }
+ }
+ },
"spec_version": {
"type": "string",
"enum": [
@@ -109,31 +129,9 @@
"marking_ref"
]
}
- },
- "definition_type": {
- "type": "string",
- "description": "The definition_type property identifies the type of Marking Definition.",
- "$ref": "#/definitions/marking-definition-type"
- },
- "definition": {
- "type": "object",
- "description": "The definition property contains the marking object itself.",
- "properties": {
- "statement": {
- "type": "string",
- "defintion": "A Statement (e.g., copyright, terms of use) applied to the content marked by this marking definition."
- },
- "tlp": {
- "type": "string",
- "defintion": "The TLP level of the content marked by this marking definition, as defined in this section.",
- "$ref": "#/definitions/tlp-level"
- }
- }
- }
+ }
},
"required": [
- "definition",
- "definition_type"
],
"definitions": {
"marking-definition-type": {
diff --git a/src/static/jsedit/relationship.json b/src/static/jsedit/relationship.json
index 048d7772..541a1a8a 100644
--- a/src/static/jsedit/relationship.json
+++ b/src/static/jsedit/relationship.json
@@ -23,7 +23,6 @@
"description": "The name used to identify the type of relationship.",
"pattern": "^[a-z0-9\\-]+$",
"enum": [
-
"uses",
"mitigates",
"indicates",
@@ -70,8 +69,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/report.json b/src/static/jsedit/report.json
index 8f2c43ed..1b3c3e15 100644
--- a/src/static/jsedit/report.json
+++ b/src/static/jsedit/report.json
@@ -34,7 +34,7 @@
"type": "string",
"$ref": "#/definitions/report-type-ov"
},
- "minItems": 1
+ "minItems": 0
},
"published": {
"type": "string",
@@ -52,8 +52,7 @@
"required": [
"name",
"object_refs",
- "published",
- "report_types"
+ "published"
],
"definitions": {
"report-type-ov": {
diff --git a/src/static/jsedit/threat-actor-relationship.json b/src/static/jsedit/threat-actor-relationship.json
index 1d151bfb..aaf98b32 100644
--- a/src/static/jsedit/threat-actor-relationship.json
+++ b/src/static/jsedit/threat-actor-relationship.json
@@ -39,8 +39,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/threat-actor.json b/src/static/jsedit/threat-actor.json
index aba6de76..e61f4f9e 100644
--- a/src/static/jsedit/threat-actor.json
+++ b/src/static/jsedit/threat-actor.json
@@ -107,8 +107,7 @@
}
},
"required": [
- "name",
- "threat_actor_types"
+ "name"
],
"definitions": {
"threat-actor-role-ov": {
diff --git a/src/static/jsedit/tool-relationship.json b/src/static/jsedit/tool-relationship.json
index 29b4a323..70da94d8 100644
--- a/src/static/jsedit/tool-relationship.json
+++ b/src/static/jsedit/tool-relationship.json
@@ -36,8 +36,8 @@
},
"description": {
"type": "string",
- "description": "A description that helps provide context about the relationship.",
- "format": "textbox"
+ "format": "textarea",
+ "description": "A description that helps provide context about the relationship."
},
"source_ref": {
"title": "source_ref",
diff --git a/src/static/jsedit/tool.json b/src/static/jsedit/tool.json
index a5dec0bc..b42abd8d 100644
--- a/src/static/jsedit/tool.json
+++ b/src/static/jsedit/tool.json
@@ -51,14 +51,13 @@
"kill_chain_phases": {
"type": "array",
"description": "The list of kill chain phases for which this Tool instance can be used.",
- "$ref": "static/jsedit/common/kill-chain-phase.json",
+ "$ref": "static/jsedit/kill-chain-phase.json",
"minItems": 1
}
},
"required": [
- "name",
- "tool_types"
+ "name"
],
"definitions": {
"tool-type-ov": {
diff --git a/src/static/jsedit/vulnerability.json b/src/static/jsedit/vulnerability.json
index 5e37e3da..3984d0fb 100644
--- a/src/static/jsedit/vulnerability.json
+++ b/src/static/jsedit/vulnerability.json
@@ -3,7 +3,7 @@
"title": "vulnerability",
"description": "A Vulnerability is a mistake in software that can be directly used by a hacker to gain access to a system or network.",
"type": "object",
- "$ref": "static/jsedit/core.json",
+ "$ref": "core.json",
"properties": {
"type": {
"type": "string",
@@ -24,6 +24,7 @@
},
"description": {
"type": "string",
+ "format": "textarea",
"description": "A description that provides more details and context about the Vulnerability."
}
},
diff --git a/src/stix/index.ts b/src/stix/index.ts
index d6faf310..ffaa08e4 100644
--- a/src/stix/index.ts
+++ b/src/stix/index.ts
@@ -16,7 +16,8 @@ import {
import {
StixRelationshipData, StixRelationship,
DataSourceType, IStixNode, StixNode,
- node_img, StixNodeData } from './stixnode';
+ node_img, StixNodeData, VisualEdgeData,
+ VisualEdge } from './stixnode';
export {
types, objects, BundleType, Relationship,
@@ -26,4 +27,5 @@ export {
StixRelationship, DataSourceType, IStixNode,
StixNode, SDO, SRO, Indicator,
ObservedData, Report, node_img, StixNodeData,
+ VisualEdgeData, VisualEdge
};
diff --git a/src/stix/stix2.ts b/src/stix/stix2.ts
index 500da2f8..4ba33959 100644
--- a/src/stix/stix2.ts
+++ b/src/stix/stix2.ts
@@ -4,12 +4,13 @@ Copyright 2018 Southern California Edison Company
ALL RIGHTS RESERVED
*/
+import { PositionDimension } from 'cytoscape';
import * as uuid from 'uuid';
export interface BundleType {
type: 'bundle' | 'Bundle';
objects: Core[];
- metadata?: Core[];
+ metadata?: {id: string, position: {x: number, y: number}}[];
}
export type SDO = Asset | AttackPattern | Campaign | CourseOfAction | Identity | Indicator | IntrusionSet | Malware | ObservedData | Report | ThreatActor | Tool | Vulnerability;
diff --git a/src/stix/stix_schemas.ts b/src/stix/stix_schemas.ts
index befbf0ae..84a61032 100644
--- a/src/stix/stix_schemas.ts
+++ b/src/stix/stix_schemas.ts
@@ -7,6 +7,7 @@ const schemas = ["asset.json",
"core.json",
"course-of-action.json",
"course-of-action-relationship.json",
+ "extension-definition.json",
"grouping.json",
"identity.json",
"identity-relationship.json",
diff --git a/src/stix/stixnode.ts b/src/stix/stixnode.ts
index bdd0211c..12852de4 100644
--- a/src/stix/stixnode.ts
+++ b/src/stix/stixnode.ts
@@ -52,6 +52,8 @@ export class StixNode implements IStixNode {
*/
constructor(the_data: StixNodeData, the_type: StixType, d_source: DataSourceType) {
+ //console.log(" type: ", the_data.name, the_type)
+
this.data = {
id: the_data.id,
label: the_type === 'marking-definition' ? the_type : the_data.name,
@@ -76,7 +78,7 @@ export class StixNode implements IStixNode {
x: 100,
y: 100,
};
- const style: CSSStyleDeclaration = { backgroundImage: node_img[the_type], backgroundFit: 'contain' } as CSSStyleDeclaration;
+ const style: CSSStyleDeclaration = { backgroundImage: node_img[the_type] } as unknown as CSSStyleDeclaration;
this.style = style;
if (this.data.data_source === 'DB' || this.data.data_source === 'IGNORE') {
this.saved = true;
@@ -118,3 +120,22 @@ export class StixRelationship implements IStixRelationship {
this.data.saved = this.saved;
}
}
+
+export interface VisualEdgeData extends cytoscape.EdgeDataDefinition {
+ raw_data: string;
+ target: Identifier;
+ source: Identifier;
+ id: string;
+}
+
+export interface IVisualEdge extends cytoscape.EdgeDefinition {
+ data: VisualEdgeData;
+}
+
+export class VisualEdge implements IVisualEdge {
+ public data: VisualEdgeData;
+
+ constructor(the_data: VisualEdgeData) {
+ this.data = the_data;
+ }
+}
\ No newline at end of file
diff --git a/src/storage/database-configuration-storage.ts b/src/storage/database-configuration-storage.ts
index fe294462..6f8b300d 100644
--- a/src/storage/database-configuration-storage.ts
+++ b/src/storage/database-configuration-storage.ts
@@ -4,7 +4,9 @@ Copyright 2018 Southern California Edison Company
ALL RIGHTS RESERVED
*/
-import ElectronStore = require("electron-store");
+import { textChangeRangeIsUnchanged } from "typescript";
+
+// import ElectronStore = require("electron-store");
export interface IDatabaseConfigOptions {
host: string;
@@ -26,6 +28,14 @@ export interface IDatabaseConfigurationStorageStructure {
configs: IDatabaseConfigMap;
}
+export interface TaxiiParams {
+ url: string;
+ apiroot_name: string;
+ collection_id: string;
+ username: string;
+ password: string;
+}
+
/**
* @description Stores database configuration
* @export
@@ -33,12 +43,55 @@ export interface IDatabaseConfigurationStorageStructure {
*/
export class DatabaseConfigurationStorage {
private static instance: DatabaseConfigurationStorage;
- private store: ElectronStore;
- private constructor() {
- this.store = new ElectronStore({ name: "stig/db_config" });
- if (!this.store.has("configs") || (Object.keys(this.store.get("configs")).length === 0)) {
- this.create_default();
+ // private store: ElectronStore;
+ private store: IDatabaseConfigurationStorageStructure;
+ // private constructor() {
+ // // this.store = new ElectronStore({ name: "stig/db_config" });
+
+ // fetch('/data', {
+ // method: 'GET',
+ // body: JSON.stringify({name: 'dbConfig'})
+ // }).then(response => response.json())
+ // .then(data => {
+ // if (data) {
+ // this.store = JSON.parse(data)
+ // } else {
+ // this.create_default();
+ // }
+ // })
+ // }
+
+ public async getConfigs() {
+ if (!this.store) {
+ let config = await fetch('/data?name=dbConfig', {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }).then(response => response.json())
+ // console.log(config.configs)
+ if (!config.configs) {
+ this.create_default()
+ } else {
+ this.store = config
+ }
+
+
}
+
+ //console.log(" store: ", JSON.stringify(this.store))
+
+ return this.store
+ }
+
+ private saveConfigs() {
+ fetch('/save', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({name: 'dbConfig', data: this.store})
+ })
}
public create_default() {
@@ -53,8 +106,8 @@ export class DatabaseConfigurationStorage {
const configmap: IDatabaseConfigMap = {
[initial.name]: initial,
};
- this.store.set('configs', configmap);
- this.current = 'stig';
+ this.store = {configs: configmap, current: 'stig'}
+ this.saveConfigs();
}
static get Instance(): DatabaseConfigurationStorage {
@@ -70,40 +123,42 @@ export class DatabaseConfigurationStorage {
* @memberof ConfigurationStorageService
*/
public save(options: IDatabaseConfigOptions) {
- const configs = this.store.get('configs');
- configs[options.name] = options;
- this.store.set('configs', configs);
+ this.store.configs[options.name] = options;
+ this.saveConfigs();
}
public get(key: string): IDatabaseConfigOptions {
- return this.configs[key];
+ return this.store.configs[key];
}
public keys(): string[] {
- return Object.keys(this.configs);
+ // console.log("Database store: ", this.store)
+ return Object.keys(this.store.configs);
}
public get current(): string {
- return this.store.get('current', undefined);
+ return this.store.current;
}
public set current(name: string) {
- this.store.set('current', name);
+ this.store.current = name;
+ this.saveConfigs();
}
public get configs(): IDatabaseConfigMap {
- return this.store.get('configs');
+ return this.store.configs;
}
public currentConfig(): IDatabaseConfigOptions {
- return this.configs[this.current];
+ return this.store.configs[this.store.current];
}
public removeConfig(name: string) {
- const configs = this.store.get('configs');
+ const configs = this.store.configs;
if (name in configs) {
delete configs[name];
- this.store.set('configs', configs);
+ this.store.configs = configs;
+ this.saveConfigs();
}
}
}
diff --git a/src/storage/query-storage.ts b/src/storage/query-storage.ts
index 635da404..e79f1dff 100644
--- a/src/storage/query-storage.ts
+++ b/src/storage/query-storage.ts
@@ -4,7 +4,7 @@ Copyright 2018 Southern California Edison Company
ALL RIGHTS RESERVED
*/
-import ElectronStore = require("electron-store");
+// import ElectronStore = require("electron-store");
interface IQueryStore {
queries: string[];
@@ -18,13 +18,51 @@ interface IQueryStore {
export class QueryStorageService {
private static instance: QueryStorageService;
- private store: ElectronStore;
+ private store: IQueryStore;
- private constructor() {
- this.store = new ElectronStore({ name: "db_queries" });
- if (!this.store.has("queries")) {
- this.store.set("queries", []);
+ // private constructor() {
+
+ // fetch('/data', {
+ // method: 'GET'
+ // }).then(response => response.json())
+ // .then(data => {
+ // if (data) {
+ // this.store = data
+ // } else {
+ // this.store = {queries: []}
+ // }
+ // })
+ // }
+
+ public async getQueryHistory() {
+ if (!this.store) {
+ let queries = await fetch('/data?name=queryStorage', {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }).then(response => response.json())
+ if (!queries.queries) {
+ this.store = {queries: []}
+ } else {
+ this.store = queries
+ }
+
}
+
+ //console.log(" store: ", JSON.stringify(this.store))
+
+ return this.store
+ }
+
+ private saveQueries() {
+ fetch('/save', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({name: 'queryStorage', data: this.store})
+ })
}
static get Instance(): QueryStorageService {
@@ -40,7 +78,7 @@ export class QueryStorageService {
* @memberof QueryStorageService
*/
public getQueries(): string[] {
- return this.store.get('queries');
+ return this.store.queries;
}
/**
@@ -50,14 +88,15 @@ export class QueryStorageService {
*/
public add(query: string) {
// this.store.delete(query);
- const queries = this.store.get('queries', []);
+ const queries = this.store.queries;
query = query.trim();
const index = queries.indexOf(query);
if ( index >= 0 ) {
queries.splice(index, 1);
}
queries.unshift(query);
- this.store.set("queries", queries);
+ this.store.queries = queries;
+ this.saveQueries()
}
/**
@@ -66,20 +105,22 @@ export class QueryStorageService {
* @memberof QueryStorageService
*/
public remove(query: string) {
- const queries = this.store.get('queries', []);
+ const queries = this.store.queries;
const index = queries.indexOf(query);
if (index !== undefined) {
queries.splice(index, 1);
- this.store.set("queries", queries);
+ this.store.queries = queries;
+ this.saveQueries()
}
}
public removeQueryByIndex(index: number): void {
- const queries = this.store.get('queries', []);
+ const queries = this.store.queries;
if ( isNaN(index) || index < 0 || index > queries.length - 1) {
return;
}
queries.splice(index, 1);
- this.store.set("queries", queries);
+ this.store.queries = queries;
+ this.saveQueries();
}
}
diff --git a/src/storage/stig-settings-storage.ts b/src/storage/stig-settings-storage.ts
index 228ef955..a50aa43b 100644
--- a/src/storage/stig-settings-storage.ts
+++ b/src/storage/stig-settings-storage.ts
@@ -4,23 +4,59 @@ Copyright 2018 Southern California Edison Company
ALL RIGHTS RESERVED
*/
-import ElectronStore = require("electron-store");
-import { Rectangle } from 'electron';
+import e from "express";
+
+// import ElectronStore = require("electron-store");
+// import { Rectangle } from 'electron';
export interface IStigSettingsOptions {
layout: string;
- bounds: Rectangle;
- maximize: boolean;
- fullScreen: boolean;
+ // bounds: Rectangle;
+ // maximize: boolean;
+ // fullScreen: boolean;
}
export class StigSettings {
private static instance: StigSettings;
- private store: ElectronStore;
+ private store: IStigSettingsOptions;
+
+ public async getSettings() {
+ if (!this.store) {
+ let settings = await fetch('/data?name=stigSettings', {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }).then(response => response.json())
+ //console.log(settings.layout)
+ if (!settings.layout) {
+ settings = {layout: "grid"}
+ this.saveSettings()
+ }
+
+ this.store = settings
+
+ }
+
+ //console.log(" store: ", JSON.stringify(this.store))
+
+ return this.store
+ }
+
+ private saveSettings() {
+ //console.log("Saving settings: ", JSON.stringify(this.store))
+ fetch('/save', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({name: 'stigSettings', data: this.store})
+ })
+ }
- private constructor() {
- this.store = new ElectronStore({ name: "stig_config" });
- if (!this.store.has("layout")) { this.store.set("layout", "Grid"); }
+ public setLayout(layout: string) {
+ this.store.layout = layout;
+ this.saveSettings();
}
static get Instance(): StigSettings {
@@ -31,11 +67,12 @@ export class StigSettings {
}
public set layout(layout: string) {
- this.store.set("layout", layout);
+ this.store.layout = layout;
+ this.saveSettings()
}
public get layout(): string {
- return this.store.get("layout", "Grid");
+ return this.store?.layout;
}
}
diff --git a/src/types/cytoscape-ngraph.forcelayout.d.ts b/src/types/cytoscape-ngraph.forcelayout.d.ts
index 6fd3b0f0..a96d23d5 100644
--- a/src/types/cytoscape-ngraph.forcelayout.d.ts
+++ b/src/types/cytoscape-ngraph.forcelayout.d.ts
@@ -4,4 +4,4 @@ Copyright 2018 Southern California Edison Company
ALL RIGHTS RESERVED
*/
-declare module 'cytoscape-ngraph.forcelayout';
+// declare module 'cytoscape-ngraph.forcelayout';
diff --git a/src/types/electron-unhandled.d.ts b/src/types/electron-unhandled.d.ts
deleted file mode 100644
index 1b16df78..00000000
--- a/src/types/electron-unhandled.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
-Copyright 2018 Southern California Edison Company
-
-ALL RIGHTS RESERVED
-*/
-
-declare module 'electron-unhandled';
diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts
index cc9edbba..11052a05 100644
--- a/src/types/globals.d.ts
+++ b/src/types/globals.d.ts
@@ -4,11 +4,11 @@ Copyright 2018 Southern California Edison Company
ALL RIGHTS RESERVED
*/
-import { Core, LayoutOptions } from 'cytoscape'
-import { WindowManager } from '../ui/window-manager';
-import { MenuItem } from 'electron';
+// import { Core, LayoutOptions } from 'cytoscape'
+// import { WindowManager } from '../ui/window-manager';
+// import { MenuItem } from 'electron';
import { ViewUtilitiesOptions } from '../graph/graphOptions';
-import { bool } from 'aws-sdk/clients/signer';
+// import { bool } from 'aws-sdk/clients/signer';
export type Optional = T | null | undefined;
diff --git a/src/types/json-editor.d.ts b/src/types/json-editor.d.ts
new file mode 100644
index 00000000..312686e0
--- /dev/null
+++ b/src/types/json-editor.d.ts
@@ -0,0 +1,120 @@
+declare module '@json-editor/json-editor/dist/jsoneditor.js';
+
+// The following types were found here:
+// https://github.com/json-editor/json-editor/issues/127#issuecomment-554503424
+
+
+declare interface SchemaOptions {
+ properties: {
+ fontSize: {
+ options: {
+ choices_options: any;
+ };
+ };
+ };
+}
+
+declare type CSSIntegrationTypes =
+ | "barebones"
+ | "html"
+ | "bootstrap2"
+ | "bootstrap3"
+ | "bootstrap4"
+ | "foundation3"
+ | "foundation4"
+ | "foundation5"
+ | "foundation6"
+ | "jqueryui"
+ | "materialize";
+
+declare type IconLibraries =
+ | "bootstrap2"
+ | "bootstrap3"
+ | "foundation2"
+ | "foundation3"
+ | "jqueryui"
+ | "fontawesome3"
+ | "fontawesome4"
+ | "fontawesome5"
+ | "materialicons";
+
+declare interface JSONEditorOptions {
+ // If true, JSON Editor will load external URLs in $ref via ajax. false
+ ajax?: boolean;
+ // Allows schema references to work either with or without cors;
+ // set to protocol://host:port when api is served by different host.
+ ajaxBase?: string;
+ // If true, JSON Editor will make ajax call with
+ // [credentials](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials). false
+ ajaxCredentials?: boolean;
+ // If true, the label will not be displayed/added. false
+ compact?: boolean;
+ // If true, remove all "add row" buttons from arrays. false
+ disable_array_add?: boolean;
+ // If true, remove all "delete row" buttons from arrays. false
+ disable_array_delete?: boolean;
+ // If true, remove all "move up" and "move down" buttons from arrays. false
+ disable_array_reorder?: boolean;
+ // If true, add copy buttons to arrays. false
+ enable_array_copy?: boolean;
+ // If true, remove all collapse buttons from objects and arrays. false
+ disable_collapse?: boolean;
+ // If true, remove all Edit JSON buttons from objects. false
+ disable_edit_json?: boolean;
+ // If true, remove all Edit Properties buttons from objects. false
+ disable_properties?: boolean;
+ // If true, array controls (add, delete etc) will be displayed at top of list. false
+ array_controls_top?: boolean;
+ // The first part of the `name` attribute of form inputs in the editor.
+ // An full example name is`root[person][name]` where "root" is the form_name_root.root
+ form_name_root?: string;
+ // The icon library to use for the editor. See the CSS Integration section below for more info. null
+ iconlib?: IconLibraries | null;
+ // Display only icons in buttons. This works only if iconlib is set. false
+ remove_button_labels?: boolean;
+ // If true, objects can only contain properties defined with the properties keyword. false
+ no_additional_properties?: boolean;
+ // An object containing schema definitions for URLs. Allows you to pre-define external schemas. {}
+ refs?: any;
+ // If true, all schemas that don't explicitly set the required property will be required. false
+ required_by_default?: boolean;
+ // If true, makes oneOf copy properties over when switching. true
+ keep_oneof_values?: boolean;
+ // A valid JSON Schema to use for the editor. Version 3 and Version 4 of the draft specification are supported. {}
+ schema?: any;
+ // When to show validation errors in the UI. Valid values are interaction, change, always, and never. "interaction"
+ show_errors?: "interaction" | "change" | "always" | "never";
+ // Seed the editor with an initial value. This should be valid against the editor's schema. null
+ startval?: any;
+ // The JS template engine to use. See the Templates and Variables section below for more info. default
+ template?:
+ | "ejs"
+ | "handlebars"
+ | "hogan"
+ | "markup"
+ | "mustache"
+ | "swig"
+ | "underscore";
+ // The CSS theme to use. See the CSS Integration section below for more info. html
+ theme?: CSSIntegrationTypes;
+ // If true, only required properties will be included by default. false
+ display_required_only?: boolean;
+ // If true, NON required properties will have an extra toggable checkbox near the title that determines if the value must be included or not in the editor´s value false
+ show_opt_in?: boolean;
+ // If true, displays a dialog box with a confirmation message before node deletion. true
+ prompt_before_delete?: boolean;
+ // The default value of `format` for objects. If set to table for example, objects will use table layout if `format` is not specified. normal
+ object_layout?: "normal" | "table" | "grid";
+}
+
+declare class JSONEditor {
+ constructor(element: Element, options?: JSONEditorOptions);
+
+ static defaults: {
+ options: JSONEditorOptions;
+ };
+
+ getValue(): any;
+ on(event: string, callback: () => void): void;
+ validate(): string[];
+}
\ No newline at end of file
diff --git a/src/ui/clipboard.ts b/src/ui/clipboard.ts
index c9d40418..4cfdb0b6 100644
--- a/src/ui/clipboard.ts
+++ b/src/ui/clipboard.ts
@@ -4,21 +4,27 @@ Copyright 2018 Southern California Edison Company
ALL RIGHTS RESERVED
*/
-import { clipboard, ipcRenderer } from 'electron';
+// import { clipboard, ipcRenderer } from 'electron';
import { BundleType, StixObject } from '../stix';
import { GraphUtils } from '../graph/graphFunctions';
-import { StigDB } from '../db/db';
-import { DatabaseConfigurationStorage } from '../storage';
+// import { StigDB } from '../db/db';
+// import { DatabaseConfigurationStorage } from '../storage';
import { StigSettings } from '../storage/stig-settings-storage';
import { JSONValue } from '../types/globals';
-ipcRenderer.on("copy_selected", (_event: Electron.Event) => {
- graph_copy();
-});
+// ipcRenderer.on("copy_selected", (_event: Electron.Event) => {
+// graph_copy();
+// });
-ipcRenderer.on("paste_elements", (_event: Electron.Event) => {
- graph_paste();
-});
+// ipcRenderer.on("paste_elements", (_event: Electron.Event) => {
+// graph_paste();
+// });
+
+let clipboard = {
+ data: "",
+ writeText: (text: string) => {clipboard.data = text},
+ readText: () => {return clipboard.data}
+}
export function graph_copy(): void {
const copied: JSONValue[] = [];
@@ -45,18 +51,18 @@ export function graph_paste(): void {
} else if (parsed.hasOwnProperty('type') && parsed.type !== 'bundle' ) {
test_stix(parsed) ? bundle.objects = [parsed] as StixObject[] : bundle.objects = [];
}
- const db = new StigDB(DatabaseConfigurationStorage.Instance.current);
- const graph = new GraphUtils(window.cycore, db);
+ // const db = new StigDB(DatabaseConfigurationStorage.Instance.current);
+ const graph = new GraphUtils(window.cycore);//, db);
graph.buildNodes(bundle);
- graph.myLayout(StigSettings.Instance.layout.toLowerCase());
+ graph.myLayout(StigSettings.Instance.layout);
return;
} catch {
return;
}
}
-ipcRenderer.on("cut_selected", () => {
- const selected = window.cycore.$(':selected');
- graph_copy();
- window.cycore.remove(selected);
-});
+// ipcRenderer.on("cut_selected", () => {
+// const selected = window.cycore.$(':selected');
+// graph_copy();
+// window.cycore.remove(selected);
+// });
diff --git a/src/ui/configuration-dialog-main.ts b/src/ui/configuration-dialog-main.ts
deleted file mode 100644
index 06cbf37a..00000000
--- a/src/ui/configuration-dialog-main.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-Copyright 2018 Southern California Edison Company
-
-ALL RIGHTS RESERVED
- */
-
-import { BrowserWindow, ipcMain } from "electron";
-import * as path from "path";
-import { DatabaseConfigurationStorage } from "../storage";
-import { IDatabaseConfigOptions } from '../storage/database-configuration-storage';
-
-export function openDatabaseConfigurationDialogMain(parent: BrowserWindow) {
- parent.webContents.send("OpenDatabaseConfiguration");
-}
-
-export function newDatabaseDialogOld(parent: BrowserWindow) {
- let promptWindow = new BrowserWindow({
- width: 640, height: 480,
- parent,
- show: false,
- modal: true,
- alwaysOnTop: true,
- title: "Database Configuration",
- autoHideMenuBar: true,
- webPreferences: {
- nodeIntegration: true,
- sandbox: false,
- },
- });
-
- promptWindow.once("closed", (_event: Electron.Event, _data: any) => {
- promptWindow = null;
- });
-
- ipcMain.on("DialogReady", (event: Electron.Event, _data: any) => {
- event.returnValue = DatabaseConfigurationStorage.Instance.currentConfig();
- });
-
- // Load the HTML dialog box
- promptWindow.loadURL("file://" + path.join(__dirname, "../static/new_database.html"));
-
- promptWindow.once("ready-to-show", () => {
- ipcMain.on("closeDialog", (_event: Electron.Event, data: IDatabaseConfigOptions) => {
- if (data !== undefined && data !== null) {
- DatabaseConfigurationStorage.Instance.save(data);
- DatabaseConfigurationStorage.Instance.current = data.name;
- parent.webContents.send('database_reconfigured', data);
- }
- });
- const config_options = DatabaseConfigurationStorage.Instance.currentConfig();
- config_options.name = '';
- promptWindow.webContents.send("config_options", config_options);
- promptWindow.show();
- });
-}
diff --git a/src/ui/configuration-dialog-renderer.ts b/src/ui/configuration-dialog-renderer.ts
deleted file mode 100644
index ed2c0d92..00000000
--- a/src/ui/configuration-dialog-renderer.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-Copyright 2018 Southern California Edison Company
-
-ALL RIGHTS RESERVED
- */
-
- import { ipcRenderer } from "electron";
- import { JSONObject } from '../types/globals';
-
-// Renderer process
-
- function cancel() {
- ipcRenderer.send("closeDialog", undefined);
- close();
-}
-
- function send_data_close(_event: MouseEvent) {
- const options = {
- host: (document.getElementById("host") as HTMLInputElement).value,
- port: (document.getElementById("port") as HTMLInputElement).value,
- name: (document.getElementById("db_name") as HTMLInputElement).value,
- username: (document.getElementById("userid") as HTMLInputElement).value,
- password: (document.getElementById("password") as HTMLInputElement).value,
- useToken: true,
- };
- ipcRenderer.send("closeDialog", options);
- }
-
- ipcRenderer.on('config_options', (_event: Electron.Event, _opt: JSONObject) => {
- const options = ipcRenderer.sendSync("DialogReady", "");
- const params = JSON.parse(options);
- document.getElementById('host').innerHTML = params.host;
- document.getElementById('port').innerHTML = params.port;
- document.getElementById('db_name').innerHTML = params.db_name;
- document.getElementById('userid').innerHTML = params.userid;
- document.getElementById('password').innerHTML = params.password;
- });
-
- document.getElementById('btn-ok').onclick = send_data_close;
- document.getElementById('btn-cancel').onclick = cancel;
diff --git a/src/ui/connect-taxii-widget.ts b/src/ui/connect-taxii-widget.ts
new file mode 100644
index 00000000..551e54b8
--- /dev/null
+++ b/src/ui/connect-taxii-widget.ts
@@ -0,0 +1,161 @@
+/*
+Copyright 2018 Southern California Edison Company
+
+ALL RIGHTS RESERVED
+*/
+
+import { use_db, get_taxii } from "../db/dbFunctions";
+import { DatabaseConfigurationStorage } from "../storage";
+import { IDatabaseConfigOptions, TaxiiParams } from '../storage/database-configuration-storage';
+import { BundleType, StixObject } from "../stix";
+import { commit } from "../db/dbFunctions";
+//const fs = require('fs');
+//import taxiiJson from '../../temp_stix_taxii.json';
+//var spawn = require('child_process').spawn;
+
+export function openConnectTaxii(key?: string) {
+ const dbdialog = new NewTaxiiConnection($('#connect-taxii-anchor'), key);
+ dbdialog.open();
+}
+
+const body = ` URL:
+
+
+
API Root Name:
+
+ Collection ID:
+
+ Username:
+
+ Password:
+
+
+`;
+
+class NewTaxiiConnection {
+ public _storage: DatabaseConfigurationStorage;
+ public _anchor: JQuery;
+ public _list = "";
+ public _header = "";
+ public _footer = "
"; // "";
+ private useConfig: string;
+
+ constructor(anchor: JQuery, key?: string) {
+ this._storage = DatabaseConfigurationStorage.Instance;
+ this._anchor = anchor;
+ this._footer = ""; // this._createDeleteButton();
+ // this.populateHistoryDialog();
+ key === undefined ? this.useConfig = this._storage.current : this.useConfig = key;
+ }
+
+ public addToDialog() {
+ this._anchor.empty();
+ this._anchor.html(this._header + body + this._footer);
+ this.setupEventHandlers();
+ }
+
+ public setupEventHandlers() {
+ return;
+ }
+
+ public isOpen(): boolean {
+ return this._anchor.dialog( "isOpen" );
+ }
+
+ private loadData() {
+ const params = this._storage.get(this.useConfig);
+ $('#host').val(params.host);
+ $('#port').val(params.port);
+ $('#db_name').val(params.name);
+ $('#username').val(params.username);
+ $('#user_password').val(params.password);
+ }
+
+ private async saveData() {
+
+ // Get user Taxii input
+ const tax: TaxiiParams = {
+ url: $("#url").val() as string,
+ apiroot_name: $("#apiroot_name").val() as string,
+ collection_id: $("#collection_id").val() as string,
+ username: $("#tax_username").val() as string,
+ password: $("#tax_password").val() as string,
+ };
+
+ // Call backend taxii logic
+ let objects = await get_taxii(tax) //await
+
+ // Debugging
+ if (objects) {
+ $('.message-status').html(`${objects.length} TAXII objects found.`);
+ console.log("try here: ", objects)
+ } else {
+ // add error message "No found Taxii objects"
+ $('.message-status').html(`No TAXII objects found.`);
+ console.log("tried")
+ }
+
+ // Commit taxii objects to database
+ const relationships : StixObject[] = []
+ var numErrors = 0
+ var i = 0
+ $('.message-status').html(`Committing ${objects.length} to the database...`);
+ for (const obj of objects) {
+ $('.message-status').html(`Committing object ${i++}/${objects.length} ...`);
+ // Save relationships for later
+ if (obj.type == "relationship" || obj.type == "sighting") {
+ relationships.push(obj)
+ } else {
+ // Commit everything else
+ //console.log(obj)
+ let success = commit(obj)
+ if (!success) {
+ numErrors++
+ }
+ }
+ }
+ for (const rel of relationships) {
+ console.log(rel)
+ $('.message-status').html(`Committing object ${i++}/${objects.length} ...`);
+ let success = commit(rel)
+ if (!success) {
+ numErrors++
+ }
+ }
+
+ $('.message-status').html(`Successfully committed ${objects.length - numErrors} out of ${objects.length} objects.`);
+
+ this.close()
+
+
+ }
+
+ private close() {
+ if (this.isOpen()) {
+ this._anchor.dialog("close");
+ }
+ }
+
+ public open() {
+ this.addToDialog();
+ this._anchor.dialog({
+ autoOpen: true,
+ modal: true,
+ width: 'maxcontent',
+ maxWidth: window.innerWidth / 2,
+ maxHeight: window.innerHeight - 100,
+ buttons: [
+ {
+ text: 'Cancel',
+ click: () => this.close(),
+ },
+ {
+ text: 'Upload TAXII to DB',
+ click: () => this.saveData(),
+ },
+ ],
+ });
+ this._anchor.dialog("open");
+ //this.loadData();
+ }
+}
diff --git a/src/ui/database-config-widget.ts b/src/ui/database-config-widget.ts
index 420c69d9..76991f57 100644
--- a/src/ui/database-config-widget.ts
+++ b/src/ui/database-config-widget.ts
@@ -4,11 +4,12 @@ Copyright 2018 Southern California Edison Company
ALL RIGHTS RESERVED
*/
+import { use_db } from "../db/dbFunctions";
import { DatabaseConfigurationStorage } from "../storage";
-import { ipcRenderer } from 'electron';
-import { newDatabaseConfiguration } from './new-database-widget';
+import { newDatabaseConfiguration } from './new-database-widget'
export function openDatabaseConfiguration() {
+ //console.log("Open database dialog")
const dbdialog = new DatabaseConfigDialog($('#db-dialog-anchor'));
dbdialog.open();
}
@@ -69,12 +70,15 @@ class DatabaseConfigDialog {
],
});
- $(".controlgroup").controlgroup({
- direction: "vertical",
- });
+ // $(".controlgroup").controlgroup({
+ // direction: "vertical"
+ // });
+
+ $(".controlgroup").buttonset();
+
$( `#db-config-list input[type='radio']`).prop('checked', false);
- $( `#db-config-list input[type='radio'][value='${this._storage.current}']` ).prop('checked', true);
- $( "#db-config-list input[type='radio']" ).checkboxradio('refresh');
+ // $( `#db-config-list input[type='radio'][value='${this._storage.current}']` ).prop('checked', true);
+ // $( "#db-config-list input[type='radio']" ).buttonset('refresh');
}
public addToDialog() {
@@ -90,12 +94,12 @@ class DatabaseConfigDialog {
this._list += ` `;
this._anchor.empty();
this._anchor.html(this._header + this._list + this._footer);
- this.setupEventHandlers();
+ // this.setupEventHandlers();
}
- public setupEventHandlers() {
- // this.useConfig = $('#db-config-list .ui-selected')[0].innerText;
- }
+ // public setupEventHandlers() {
+ // this.useConfig = $('#db-config-list .ui-selected')[0].innerText;
+ // }
private deleteSelected() {
this._storage.removeConfig($( "#db-config-list input:checked" ).val() as string);
@@ -114,7 +118,8 @@ class DatabaseConfigDialog {
private useDatabase() {
this.useConfig = $( "#db-config-list input:checked" ).val() as string;
this._storage.current = this.useConfig;
- ipcRenderer.send('useDatabase', this._storage.get(this.useConfig));
+ // ipcRenderer.send('useDatabase', this._storage.get(this.useConfig));
+ use_db(this._storage.get(this._storage.current))
//add progress bar here tied to
this._anchor.dialog("close");
}
diff --git a/src/ui/database-upload-widget.ts b/src/ui/database-upload-widget.ts
new file mode 100644
index 00000000..b00354f2
--- /dev/null
+++ b/src/ui/database-upload-widget.ts
@@ -0,0 +1,106 @@
+import { commit } from "../db/dbFunctions";
+import { BundleType, StixObject } from "../stix";
+
+export function openDatabaseUpload() {
+ const dbUpload = new DatabaseUploadDialog($("#db-upload-anchor"))
+ dbUpload.open()
+}
+
+const body = `