diff --git a/.gitignore b/.gitignore index 8b9a8f5..a5cfe33 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ tsconfig.tsbuildinfo config.yaml .env docker/.neo4j/ +docker/.memgraph/ yarn-error.log dist/ coverage/ diff --git a/README.md b/README.md index fb824c5..f88ea8a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Starbase from [JupiterOne](https://jupiterone.com), collects assets and relationships from services and systems including cloud infrastructure, SaaS applications, security controls, and more into an intuitive graph view backed by -the [Neo4j](https://neo4j.com/) database. +the [Memgraph](https://memgraph.com/) or [Neo4j](https://neo4j.com/) database. **Security is a basic right**. Starbase's goal is to **democratize graph-based security analysis** and overall visibility into external services and systems. @@ -208,8 +208,8 @@ Commands: 2. Run `yarn starbase run` to collect data for each listed integration and then push collected data to the storage endpoint listed in `config.yaml`. -For additional information on using Neo4j or JupiterOne as a storage endpoint, -please see the [README.md](docker/README.md) provided. +For additional information on using Memgraph, Neo4j or JupiterOne as a storage +endpoint, please see the [README.md](docker/README.md) provided. ### Running Starbase - Docker @@ -276,8 +276,8 @@ Join us on `#starbase` on the [^1]: JupiterOne Starbase and the [Lyft Cartography](https://github.com/lyft/cartography) projects complement - each other as both projects push graph data to a Neo4j database instance. As - such, users of Starbase can leverage the AWS connector from Cartography to + each other as both projects push graph data to Memgraph or Neo4j database instance. + As such, users of Starbase can leverage the AWS connector from Cartography to ingest AWS assets and relationships. A more comprehensive AWS integration is used by the [cloud hosted JupiterOne platform](https://jupiterone.com) and we are considering open sourcing the JupiterOne AWS integration in the diff --git a/config.yaml.example b/config.yaml.example index f205908..9cccb49 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -16,6 +16,14 @@ integrations: config: AUTH_TOKEN: example-token ORGANIZATION_SLUG: example-organization +storage: + - + engine: memgraph + config: + username: + password: + uri: bolt://localhost:3000 + database: memgraph storage: - engine: neo4j diff --git a/docker/memgraph.yml b/docker/memgraph.yml new file mode 100644 index 0000000..35c84e6 --- /dev/null +++ b/docker/memgraph.yml @@ -0,0 +1,18 @@ +version: '3.8' +services: + memgraph: + image: 'memgraph/memgraph-platform' + hostname: memgraph + ports: + - '3000:3000' + - '7444:7444' + - '7687:7687' + volumes: + - mg_lib:/var/lib/memgraph + - mg_log:/var/log/memgraph + - mg_etc:/etc/memgraph + entrypoint: ["/usr/bin/supervisord"] +volumes: + mg_lib: + mg_log: + mg_etc: \ No newline at end of file diff --git a/package.json b/package.json index 0c9665d..c9f5aa4 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "scripts": { "starbase": "ts-node ./src/index.ts", "starbase:help": "ts-node ./src/index.ts -h", + "memgraph:start": "docker-compose -f docker/memgraph.yml up -d", + "memgraph:stop": "docker-compose -f docker/memgraph.yml down", "neo4j:start": "docker-compose -f docker/neo4j.yml up -d", "neo4j:start:plugins": "docker-compose -f docker/neo4j.yml -f docker/neo4jplugins.yml up -d", "neo4j:stop": "docker-compose -f docker/neo4j.yml down", diff --git a/src/cli/wipe.ts b/src/cli/wipe.ts index e559ba2..00a116b 100644 --- a/src/cli/wipe.ts +++ b/src/cli/wipe.ts @@ -3,10 +3,10 @@ import { executeWithLogging } from '../starbase/process'; export function wipe() { return createCommand('wipe') - .description('wipes data from Neo4j instance with specified instance ID') + .description('wipes data from database instance with specified instance ID') .requiredOption( '-i, --integration-instance-id ', - '_integrationInstanceId assigned to uploaded entities that should be wiped from Neo4j', + '_integrationInstanceId assigned to uploaded entities that should be wiped from the database', ) .option( '-db, --database-name ', @@ -14,23 +14,38 @@ export function wipe() { 'neo4j', ) .action(async (options) => { - executeWithLogging( - `yarn j1-integration neo4j wipe -i ${options.integrationInstanceId} -db ${options.databaseName}`, - ); + if (options.databaseName == 'neo4j'){ + executeWithLogging( + `yarn j1-integration neo4j wipe -i ${options.integrationInstanceId} -db ${options.databaseName}`, + ); + } + else if (options.databaseName == 'memgraph'){ + executeWithLogging( + `yarn j1-integration memgraph wipe -i ${options.integrationInstanceId} -db ${options.databaseName}`, + ); + } }); } export function wipeAll() { return createCommand('wipe-all') - .description('wipes all data from Neo4j instance') + .description('wipes all data from database instance') .option( '-db, --database-name ', 'optional database to wipe data from (only available for enterprise Neo4j databases)', 'neo4j', ) .action(async (options) => { - executeWithLogging( - `yarn j1-integration neo4j wipe-all -db ${options.databaseName}`, - ); + if (options.databaseName == 'neo4j'){ + executeWithLogging( + `yarn j1-integration neo4j wipe-all -db ${options.databaseName}`, + ); + } + else if (options.databaseName == 'memgraph'){ + executeWithLogging( + `yarn j1-integration memgraph wipe-all -db ${options.databaseName}`, + ); + } + }); } diff --git a/src/starbase/config.test.ts b/src/starbase/config.test.ts index 64898d8..4e26d4d 100644 --- a/src/starbase/config.test.ts +++ b/src/starbase/config.test.ts @@ -49,6 +49,15 @@ const testConfig: StarbaseConfig = { database: 'neo4j', }, }, + { + engine: 'memgraph', + config: { + username: '', + password: '', + uri: 'bolt://localhost:7687', + database: 'memgraph', + }, + }, ], }; diff --git a/src/starbase/config.ts b/src/starbase/config.ts index b47b0d2..5073a21 100644 --- a/src/starbase/config.ts +++ b/src/starbase/config.ts @@ -66,6 +66,16 @@ NEO4J_PASSWORD=${storage.config.password} ); } +async function writeMemgraphRootConfig(storage: StarbaseStorage) { + await fs.appendFile( + '.env', + `MEMGRAPH_URI=${storage.config.uri} + MEMGRAPH_USER=${storage.config.username} + MEMGRAPH_PASSWORD=${storage.config.password} +`, + ); +} + async function writeJ1RootConfig(storage: StarbaseStorage) { await fs.appendFile( '.env', @@ -142,6 +152,7 @@ async function parseConfigYaml(configPath: string): Promise { export { parseConfigYaml, writeIntegrationConfig, + writeMemgraphRootConfig, writeNeo4jRootConfig, writeJ1RootConfig, integrationConfigToEnvFormat, diff --git a/src/starbase/execution.ts b/src/starbase/execution.ts index c67c144..6057c96 100644 --- a/src/starbase/execution.ts +++ b/src/starbase/execution.ts @@ -1,5 +1,6 @@ import { writeIntegrationConfig, + writeMemgraphRootConfig, writeNeo4jRootConfig, writeJ1RootConfig, } from './config'; @@ -18,6 +19,9 @@ async function setupStarbaseStorageEngine(starbaseConfig: StarbaseConfig) { case 'neo4j': await writeNeo4jRootConfig(storageConfig); break; + case 'memgraph': + await writeMemgraphRootConfig(storageConfig); + break; case 'jupiterone': await writeJ1RootConfig(storageConfig); break; @@ -71,6 +75,13 @@ async function executeStarbase( } to browse your Neo4J graph.`, ); } + else if (engines.includes('memgraph')) { + console.log( + `open ${ + process.env.MEMGRAPH_BROWSER_URI ?? 'http://localhost:3000/browser/' + } to browse your graph in Memgraph.`, + ); + } } export { executeStarbase, OnSkipIntegrationExecutionFunctionParams }; diff --git a/src/starbase/integration.ts b/src/starbase/integration.ts index 15963c9..05d0fe8 100644 --- a/src/starbase/integration.ts +++ b/src/starbase/integration.ts @@ -8,7 +8,7 @@ import { StarbaseConfigurationError } from './error'; async function collectIntegrationData(integrationDirectory: string) { await executeWithLogging( - `yarn --cwd ${integrationDirectory} start --disable-schema-validation;`, + `yarn --cwd ${integrationDirectory} start --disable-schema-validation`, ); } @@ -22,6 +22,16 @@ async function writeIntegrationDataToNeo4j( ); } +async function writeIntegrationDataToMemgraph( + integrationInstanceId: string, + integrationDirectory: string, + integrationDatabase: string = 'memgraph', +) { + await executeWithLogging( + `yarn j1-integration memgraph push -i ${integrationInstanceId} -d ${integrationDirectory}/.j1-integration -db ${integrationDatabase}`, + ); +} + async function writeIntegrationDataToJupiterOne( integrationInstanceId: string, integrationDirectory: string, @@ -52,6 +62,13 @@ async function executeIntegration( storageConfig.config?.database, ); break; + case 'memgraph': + await writeIntegrationDataToMemgraph( + integration.instanceId, + integration.directory, + storageConfig.config?.database, + ); + break; case 'jupiterone': await writeIntegrationDataToJupiterOne( integration.instanceId, @@ -70,5 +87,6 @@ async function executeIntegration( export { executeIntegration, collectIntegrationData, + writeIntegrationDataToMemgraph, writeIntegrationDataToNeo4j, }; diff --git a/src/starbase/types.ts b/src/starbase/types.ts index f8a283e..b68b7dc 100644 --- a/src/starbase/types.ts +++ b/src/starbase/types.ts @@ -18,6 +18,13 @@ interface Neo4jStorageEngineConfig { database?: string; } +interface MemgraphStorageEngineConfig { + username: string; + password: string; + uri: string; + database?: string; +} + interface JupiterOneStorageEngineConfig { /** * The JupiterOne API key to authenticate with @@ -35,6 +42,8 @@ interface JupiterOneStorageEngineConfig { export type Neo4jStorage = StarbaseStorage; +export type MemgraphStorage = StarbaseStorage; + export type JupiterOneStorage = StarbaseStorage; export type StarbaseConfig = {