1+ const { JupiterOneClient } = require ( "@jupiterone/jupiterone-client-nodejs" )
2+ const { v4 : uuid } = require ( 'uuid' ) ;
3+ const fs = require ( "fs" )
4+
5+
6+ const authenticate = async ( ) => {
7+ console . log ( 'Authenticating...' ) ;
8+
9+ const input = {
10+ accessToken : process . env . J1_ACCESS_TOKEN ,
11+ account : process . env . J1_ACCOUNT ,
12+ dev : process . env . J1_DEV_ENABLED
13+ } ;
14+
15+ const j1 = new JupiterOneClient ( input ) ;
16+
17+ await j1 . init ( ) ;
18+
19+ console . log ( 'Successfully authenticated...' ) ;
20+
21+ return j1 ;
22+ } ;
23+
24+ const j1UniqueKeyFileLocation = `${ process . cwd ( ) } /j1-scope-key`
25+
26+ const getScope = ( ) => {
27+ let scope ;
28+ if ( ! fs . existsSync ( j1UniqueKeyFileLocation ) ) {
29+ scope = uuid ( )
30+ fs . writeFileSync ( j1UniqueKeyFileLocation , scope , 'utf8' )
31+ } else {
32+ scope = fs . readFileSync ( j1UniqueKeyFileLocation , 'utf8' )
33+ }
34+
35+ return scope ;
36+ }
37+
38+ // TODO: make this as a config value so shell script and node program reference same file location
39+ const inputDataFilePath = "./results"
40+
41+ const sentinelDictionary = {
42+ 0 : "ip" ,
43+ 1 : "hostname" ,
44+ 2 : "appname" ,
45+ 3 : "team" ,
46+ 4 : "ignore (y/n)" ,
47+ 5 : "comments" ,
48+ 6 : "md5hash" ,
49+ 7 : "timestamp" ,
50+ 8 : "container" ,
51+ 9 : "image" ,
52+ 10 : "fullpath" ,
53+ 11 : "version" ,
54+ }
55+
56+ const main = async ( ) => {
57+ // Bail early if file doesn't exist
58+ // This indicates a problem upstream
59+ if ( ! fs . existsSync ( inputDataFilePath ) ) return
60+
61+ // utf8 guarantees our output is returned to us as a string
62+ const input = fs . readFileSync ( inputDataFilePath , "utf8" ) ?. trim ( )
63+
64+ const j1 = await authenticate ( )
65+ const scope = getScope ( ) ;
66+
67+ // Ensure at least we have an array contains empty string
68+ const lines = input ?. length ? input . split ( / \n / ) : [ ]
69+
70+ // Create entities to upload to J1
71+ const entities = lines . map ( ( line ) => {
72+ const sentinelDataProps = line ?. toString ( ) . split ( "," ) ?? [ ]
73+
74+ return {
75+ _key : uuid ( ) ,
76+ _type : 'log4j_vulnerability' ,
77+ _class : 'Finding' ,
78+ displayName : sentinelDataProps [ 11 ] ,
79+ [ sentinelDictionary [ 0 ] ] : process . env . HOST_IP || sentinelDataProps [ 0 ] ,
80+ [ sentinelDictionary [ 1 ] ] : process . env . HOST_IDENTIFIER || sentinelDataProps [ 1 ] ,
81+ [ sentinelDictionary [ 6 ] ] : sentinelDataProps [ 6 ] ,
82+ [ sentinelDictionary [ 7 ] ] : sentinelDataProps [ 7 ] ,
83+ [ sentinelDictionary [ 8 ] ] : sentinelDataProps [ 8 ] === 'true' ,
84+ [ sentinelDictionary [ 9 ] ] : sentinelDataProps [ 9 ] ,
85+ [ sentinelDictionary [ 10 ] ] : sentinelDataProps [ 10 ] ,
86+ [ sentinelDictionary [ 11 ] ] : sentinelDataProps [ 11 ] ,
87+ }
88+ } )
89+
90+ // Note: entities of 0 isn't necessarily a bad thing..
91+ // It's still needed to clear out existing data in the event
92+ // that there were previous vulnerabilities and they have since
93+ // been remediated.
94+ console . log ( 'Entities Uploading :>> ' , entities . length ) ;
95+
96+ await j1 . bulkUpload ( { syncJobOptions : { scope} , entities} )
97+ if ( entities . length ) {
98+ console . log ( 'Entities may be found with a J1QL query like "Find log4j_vulnerability"' )
99+ }
100+ }
101+
102+ main ( ) . catch ( console . error )
0 commit comments