Support drag and drop file ingestion
parent
d96651635d
commit
f093a6863c
1
main.js
1
main.js
|
@ -2,7 +2,6 @@ const electron = require('electron')
|
|||
var platform = require('os').platform()
|
||||
// Module to control application life.
|
||||
const app = electron.app
|
||||
const Tray = electron.Tray
|
||||
const Menu = electron.Menu
|
||||
|
||||
// Module to create native browser window.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "bloodhound",
|
||||
"version": "2.0",
|
||||
"version": "2.0.0",
|
||||
"description": "Graph Theory for Active Directory",
|
||||
"keywords": [
|
||||
"Graph",
|
||||
|
@ -58,6 +58,7 @@
|
|||
"eventemitter2": "^4.1.0",
|
||||
"jquery": "^3.2.1",
|
||||
"linkurious": "^1.5.1",
|
||||
"magic-number": "^0.1.6",
|
||||
"mustache": "^2.3.0",
|
||||
"neo4j-driver": "^1.6.2",
|
||||
"prop-types": "^15.6.2",
|
||||
|
|
|
@ -20,6 +20,19 @@ import About from "./components/Modals/About.jsx";
|
|||
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
||||
|
||||
export default class AppContainer extends Component {
|
||||
componentDidMount(){
|
||||
document.addEventListener('dragover', function(event){
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}, false)
|
||||
|
||||
document.addEventListener('drop', function(event){
|
||||
emitter.emit("filedrop", event)
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}, false)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TransitionGroup className="max">
|
||||
|
|
|
@ -39,6 +39,8 @@ export default class Login extends Component {
|
|||
|
||||
if (this.state.password !== "") {
|
||||
this.checkDBCreds();
|
||||
}else{
|
||||
this.checkDBPresence();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,8 +61,6 @@ export default class Login extends Component {
|
|||
checkDBPresence() {
|
||||
var url = this.state.url;
|
||||
var icon = this.state.icon;
|
||||
var jicon = jQuery(icon);
|
||||
var btn = jQuery(this.refs.loginButton);
|
||||
|
||||
if (url === "") {
|
||||
return;
|
||||
|
|
|
@ -12,16 +12,17 @@ import {
|
|||
buildOuJson
|
||||
} from "utils";
|
||||
import { If, Then, Else } from "react-if";
|
||||
const { dialog, app } = require("electron").remote;
|
||||
var fs = require("fs");
|
||||
var async = require("async");
|
||||
var unzip = require("unzipper");
|
||||
var fpath = require("path");
|
||||
import { remote } from "electron";
|
||||
const { dialog, app } = remote;
|
||||
import { unlinkSync, createReadStream, createWriteStream } from "fs";
|
||||
import { eachSeries } from "async";
|
||||
import { Parse } from "unzipper";
|
||||
import { join } from "path";
|
||||
|
||||
const Pick = require("stream-json/filters/Pick");
|
||||
const { streamArray } = require("stream-json/streamers/StreamArray");
|
||||
const { chain } = require("stream-chain");
|
||||
const Asm = require("stream-json/Assembler");
|
||||
import { withParser } from "stream-json/filters/Pick";
|
||||
import { streamArray } from "stream-json/streamers/StreamArray";
|
||||
import { chain } from "stream-chain";
|
||||
import { connectTo } from "stream-json/Assembler";
|
||||
|
||||
export default class MenuContainer extends Component {
|
||||
constructor() {
|
||||
|
@ -35,6 +36,42 @@ export default class MenuContainer extends Component {
|
|||
};
|
||||
|
||||
emitter.on("cancelUpload", this.cancelUpload.bind(this));
|
||||
emitter.on("filedrop", this.fileDrop.bind(this))
|
||||
}
|
||||
|
||||
fileDrop(e){
|
||||
let fileNames = []
|
||||
$.each(e.dataTransfer.files, function(_, file){
|
||||
fileNames.push({ path: file.path, name: file.name });
|
||||
})
|
||||
|
||||
this.unzipNecessary(fileNames).then(
|
||||
function(results) {
|
||||
eachSeries(
|
||||
results,
|
||||
function(file, callback) {
|
||||
emitter.emit(
|
||||
"showAlert",
|
||||
"Processing file {}".format(file.name)
|
||||
);
|
||||
this.getFileMeta(file.path, callback);
|
||||
}.bind(this),
|
||||
function done() {
|
||||
setTimeout(
|
||||
function() {
|
||||
this.setState({ uploading: false });
|
||||
}.bind(this),
|
||||
3000
|
||||
);
|
||||
$.each(results, function(_, file) {
|
||||
if (file.delete) {
|
||||
unlinkSync(file.path);
|
||||
}
|
||||
});
|
||||
}.bind(this)
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
cancelUpload() {
|
||||
|
@ -88,13 +125,13 @@ export default class MenuContainer extends Component {
|
|||
var input = jQuery(this.refs.fileInput);
|
||||
var fileNames = [];
|
||||
|
||||
$.each(input[0].files, function(index, file) {
|
||||
$.each(input[0].files, function(_, file) {
|
||||
fileNames.push({ path: file.path, name: file.name });
|
||||
});
|
||||
|
||||
this.unzipNecessary(fileNames).then(
|
||||
function(results) {
|
||||
async.eachSeries(
|
||||
eachSeries(
|
||||
results,
|
||||
function(file, callback) {
|
||||
emitter.emit(
|
||||
|
@ -112,7 +149,7 @@ export default class MenuContainer extends Component {
|
|||
);
|
||||
$.each(results, function(index, file) {
|
||||
if (file.delete) {
|
||||
fs.unlinkSync(file.path);
|
||||
unlinkSync(file.path);
|
||||
}
|
||||
});
|
||||
}.bind(this)
|
||||
|
@ -132,12 +169,11 @@ export default class MenuContainer extends Component {
|
|||
var name = files[index].name;
|
||||
|
||||
if (path.endsWith(".zip")) {
|
||||
await fs
|
||||
.createReadStream(path)
|
||||
.pipe(unzip.Parse())
|
||||
await createReadStream(path)
|
||||
.pipe(Parse())
|
||||
.on("entry", function(entry) {
|
||||
var output = fpath.join(tempPath, entry.path);
|
||||
entry.pipe(fs.createWriteStream(output));
|
||||
var output = join(tempPath, entry.path);
|
||||
entry.pipe(createWriteStream(output));
|
||||
processed.push({
|
||||
path: output,
|
||||
name: entry.path,
|
||||
|
@ -175,11 +211,11 @@ export default class MenuContainer extends Component {
|
|||
console.log(file);
|
||||
|
||||
let pipeline = chain([
|
||||
fs.createReadStream(file, { encoding: "utf8" }),
|
||||
Pick.withParser({ filter: "meta" })
|
||||
createReadStream(file, { encoding: "utf8" }),
|
||||
withParser({ filter: "meta" })
|
||||
]);
|
||||
|
||||
let asm = Asm.connectTo(pipeline);
|
||||
let asm = connectTo(pipeline);
|
||||
asm.on(
|
||||
"done",
|
||||
function(asm) {
|
||||
|
@ -199,8 +235,8 @@ export default class MenuContainer extends Component {
|
|||
|
||||
processJson(file, callback, count, type) {
|
||||
let pipeline = chain([
|
||||
fs.createReadStream(file, { encoding: "utf8" }),
|
||||
Pick.withParser({ filter: type }),
|
||||
createReadStream(file, { encoding: "utf8" }),
|
||||
withParser({ filter: type }),
|
||||
streamArray()
|
||||
]);
|
||||
|
||||
|
|
|
@ -650,7 +650,7 @@ export default class SearchContainer extends Component {
|
|||
}
|
||||
|
||||
query +=
|
||||
" WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p";
|
||||
" WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p";
|
||||
|
||||
emitter.emit("query", query, { aprop: start, bprop: end });
|
||||
}
|
||||
|
@ -809,7 +809,7 @@ export default class SearchContainer extends Component {
|
|||
}
|
||||
|
||||
query +=
|
||||
" WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p";
|
||||
" WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p";
|
||||
|
||||
emitter.emit("query", query, { aprop: start, bprop: end });
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<li onclick="emitter.emit('setEnd', '{{type}}:{{label}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
|
||||
</li>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:User {name:{name}}),(m:User),p=shortestPath((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:User {name:{name}}),(m:User),p=shortestPath((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
|
||||
</li>
|
||||
{{/type_user}} {{#type_computer}}
|
||||
|
@ -19,7 +19,7 @@
|
|||
<li onclick="emitter.emit('setEnd', '{{type}}:{{label}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
|
||||
</li>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:Computer {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:Computer {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
|
||||
</li>
|
||||
{{/type_computer}} {{#type_group}}
|
||||
|
@ -29,7 +29,7 @@
|
|||
<li onclick="emitter.emit('setEnd', '{{type}}:{{label}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
|
||||
</li>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:Group {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:Group {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
|
||||
</li>
|
||||
{{/type_group}} {{#type_gpo}}
|
||||
|
@ -39,7 +39,7 @@
|
|||
<li onclick="emitter.emit('setEnd', '{{type}}:{{guid}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
|
||||
</li>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:GPO {name:{name}}),(m),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.guid={guid} RETURN p', {name: '{{name}}'})">
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:GPO {name:{name}}),(m),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.guid={guid} RETURN p', {name: '{{name}}'})">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
|
||||
</li>
|
||||
{{/type_gpo}} {{#type_ou}}
|
||||
|
@ -49,7 +49,7 @@
|
|||
<li onclick="emitter.emit('setEnd', '{{type}}:{{guid}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
|
||||
</li>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:OU {guid:{guid}}),(m),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.guid={guid} RETURN p', {guid: '{{guid}}'})">
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:OU {guid:{guid}}),(m),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.guid={guid} RETURN p', {guid: '{{guid}}'})">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
|
||||
</li>
|
||||
{{/type_ou}} {{#type_domain}}
|
||||
|
@ -59,7 +59,7 @@
|
|||
<li onclick="emitter.emit('setEnd', '{{type}}:{{label}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
|
||||
</li>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:Domain {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}} ')">
|
||||
<li onclick="emitter.emit('query', 'MATCH (n:Domain {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}} ')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
|
||||
</li>
|
||||
{{/type_domain}} {{#expand}}
|
||||
|
|
|
@ -593,6 +593,7 @@ export function buildComputerJson(chunk) {
|
|||
let localadmins = comp.LocalAdmins;
|
||||
let rdpers = comp.RemoteDesktopUsers;
|
||||
let primarygroup = comp.PrimaryGroup;
|
||||
let dcom = comp.DcomUsers;
|
||||
|
||||
if (!queries.properties) {
|
||||
if (primarygroup === null) {
|
||||
|
@ -638,6 +639,18 @@ export function buildComputerJson(chunk) {
|
|||
let p = { name: name, target: aName };
|
||||
insert(queries, hash, statement, p);
|
||||
});
|
||||
|
||||
$.each(dcom, function(_, dcomu) {
|
||||
let aType = dcomu.Type;
|
||||
let aName = dcomu.Name;
|
||||
let rel = "ExecuteDCOM";
|
||||
|
||||
let hash = rel + aType;
|
||||
|
||||
let statement = baseQuery.format(aType, rel);
|
||||
let p = { name: name, target: aName };
|
||||
insert(queries, hash, statement, p);
|
||||
});
|
||||
});
|
||||
return queries;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue