New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
XSOAR NG CoreRestAPI support #23804
Merged
Merged
XSOAR NG CoreRestAPI support #23804
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
077a34c
fix
dansterenson 7c7a6d8
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson 2da5e90
yml fixes
dansterenson bc89da7
more fixes
dansterenson 475f8bc
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson 81e1fcd
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson a86e49e
add support to Standard and Advanced
dansterenson 664bcb3
fix readme and yml
dansterenson aeca4de
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson 842582e
update RN
dansterenson 9316e9a
Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml
dansterenson 284c1a0
support empty auth_method
dansterenson ddf0be0
Merge branch 'XSOAR-NG-update-Demisto-Rest-API' of github.com:demisto…
dansterenson de4b010
remove marketplaces field
dansterenson 9f459f8
auth ID handle and deprecate DemistoRestApi
dansterenson c4571e9
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson db72a93
update RN
dansterenson 16e3186
fix validation errors
dansterenson 357c620
remove additional info
dansterenson cd3a1fb
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson 8c893e5
more fixes
dansterenson b8ddd3a
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson 8b3f829
more fixes
dansterenson b534504
Add core-api-install-packs
dansterenson 886491a
Fix names
dansterenson 9e564de
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson a249e15
deprecate in description
dansterenson 062f8b2
deprecate in description
dansterenson edb9994
update RN
dansterenson 4451e23
more fixes
dansterenson 7b1c731
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,38 +2,95 @@ var serverURL = params.url; | |
if (serverURL.slice(-1) === '/') { | ||
serverURL = serverURL.slice(0,-1); | ||
} | ||
serverURL = serverURL + '/xsoar' | ||
|
||
sendMultipart = function (uri, entryID, body) { | ||
if (params.auth_id || (params.creds_apikey && params.creds_apikey.identifier)) { | ||
serverURL = serverURL + '/xsoar' | ||
} | ||
|
||
var marketplace_url = params.marketplace_url? params.marketplace_url : 'https://storage.googleapis.com/marketplace-dist/content/packs/' | ||
|
||
getTenantAccountName = function () { | ||
// example: for 'https://account-testing-ysdkvou:443/acc_Test' will return 'acc_Test' | ||
const urls = demistoUrls() | ||
const server_url = urls['server'].toString() | ||
// server_url example - https://account-testing-ysdkvou:443/acc_Test | ||
var account_name = '' | ||
// check if server_url contains "/acc_" string | ||
if (server_url.indexOf("/acc_") >= 0){ | ||
const words = server_url.split('acc_') | ||
const tenant_name = words[words.length - 1] | ||
if (tenant_name !== "") { | ||
account_name = 'acc_' + tenant_name | ||
} | ||
} | ||
return account_name | ||
} | ||
|
||
getStandardAuthMethodHeaders = function(key, auth_id, content_type) { | ||
return { | ||
'Authorization': [key], | ||
'x-xdr-auth-id': [auth_id], | ||
'Content-Type': [content_type], | ||
'Accept': ['application/json'] | ||
} | ||
} | ||
|
||
getAdvancedAuthMethodHeaders = function(key, auth_id, content_type,) { | ||
const nonce = Array.from({length: 64}, () => Math.random().toString(36).charAt(2)).join(""); | ||
const timestamp = Date.now().toString(); | ||
var auth_key = key + nonce + timestamp | ||
auth_key = unescape(encodeURIComponent(auth_key)); | ||
const auth_key_hash = SHA256_hash(auth_key) | ||
|
||
return { | ||
'x-xdr-timestamp': [timestamp], | ||
'x-xdr-nonce': [nonce], | ||
'x-xdr-auth-id': [auth_id], | ||
'Authorization': [auth_key_hash], | ||
'Content-Type': [content_type], | ||
'Accept': ['application/json'] | ||
} | ||
} | ||
|
||
getRequestURL = function (uri) { | ||
var requestUrl = serverURL; | ||
if (uri.slice(-1) !== '/') { | ||
if (params.use_tenant){ | ||
requestUrl += '/' + getTenantAccountName(); | ||
} | ||
if (uri.slice(0, 1) !== '/') { | ||
requestUrl += '/'; | ||
} | ||
requestUrl += uri; | ||
return requestUrl | ||
} | ||
|
||
sendMultipart = function (uri, entryID, body) { | ||
var requestUrl = getRequestURL(uri) | ||
try { | ||
body = JSON.parse(body); | ||
} catch (ex) { | ||
// do nothing, use the body as is in the request. | ||
logDebug('could not parse body as a JSON object, passing as is. body: ' + JSON.stringify(body)); | ||
} | ||
var key = [params.apikey? params.apikey : (params.creds_apikey? params.creds_apikey.password : '')]; | ||
var key = params.apikey? params.apikey : (params.creds_apikey? params.creds_apikey.password : ''); | ||
if (key == ''){ | ||
throw 'API Key must be provided.'; | ||
} | ||
var auth_id = [params.auth_id? params.auth_id : (params.creds_apikey? params.creds_apikey.identifier : '')]; | ||
if (auth_id == ''){ | ||
throw 'Auth ID must be provided.'; | ||
var auth_id = params.auth_id? params.auth_id : (params.creds_apikey? params.creds_apikey.identifier : ''); | ||
var headers = {} | ||
// in case the integration was installed before auth_method was added, the auth_method param will be empty so | ||
// we will use the standard auth method | ||
if (!params.auth_method || params.auth_method == 'Standard'){ | ||
headers = getStandardAuthMethodHeaders(key, auth_id, 'multipart/form-data') | ||
} | ||
else if (params.auth_method == 'Advanced') { | ||
headers = getAdvancedAuthMethodHeaders(key, auth_id, 'multipart/form-data') | ||
} | ||
var res = httpMultipart( | ||
requestUrl, | ||
entryID, | ||
{ | ||
Headers: { | ||
'Authorization': key, | ||
'x-xdr-auth-id': auth_id, | ||
'Content-Type': ['multipart/form-data'], | ||
'Accept': ['application/json'] | ||
}, | ||
Headers: headers, | ||
}, | ||
body, | ||
params.insecure, | ||
|
@@ -42,7 +99,7 @@ sendMultipart = function (uri, entryID, body) { | |
'file' | ||
); | ||
if (res.StatusCode < 200 || res.StatusCode >= 300) { | ||
throw 'Demisto REST APIs - Request Failed.\nStatus code: ' + res.StatusCode + '.\nBody: ' + JSON.stringify(res) + '.'; | ||
throw 'Core REST APIs - Request Failed.\nStatus code: ' + res.StatusCode + '.\nBody: ' + JSON.stringify(res) + '.'; | ||
} | ||
try { | ||
var response = res.Body; | ||
|
@@ -53,35 +110,35 @@ sendMultipart = function (uri, entryID, body) { | |
} | ||
return {response: response}; | ||
} catch (ex) { | ||
throw 'Demisto REST APIs - Error parsing response - ' + ex + '\nBody:' + res.Body; | ||
throw 'Core REST APIs - Error parsing response - ' + ex + '\nBody:' + res.Body; | ||
} | ||
|
||
}; | ||
|
||
var sendRequest = function(method, uri, body, raw) { | ||
var requestUrl = serverURL; | ||
if (uri.slice(0, 1) !== '/') { | ||
requestUrl += '/'; | ||
} | ||
requestUrl += uri; | ||
var key = [params.apikey? params.apikey : (params.creds_apikey? params.creds_apikey.password : '')]; | ||
var requestUrl = getRequestURL(uri) | ||
var key = params.apikey? params.apikey : (params.creds_apikey? params.creds_apikey.password : ''); | ||
if (key == ''){ | ||
throw 'API Key must be provided.'; | ||
} | ||
var auth_id = [params.auth_id? params.auth_id : (params.creds_apikey? params.creds_apikey.identifier : '')]; | ||
if (auth_id == ''){ | ||
throw 'Auth ID must be provided.'; | ||
var auth_id = params.auth_id? params.auth_id : (params.creds_apikey? params.creds_apikey.identifier : ''); | ||
var headers = {} | ||
// in case the integration was installed before auth_method was added, the auth_method param will be empty so | ||
// we will use the standard auth method | ||
if (!params.auth_method || params.auth_method == 'Standard'){ | ||
headers = getStandardAuthMethodHeaders(key, auth_id, 'application/json') | ||
} | ||
else if (params.auth_method == 'Advanced') { | ||
if (!auth_id) { | ||
throw 'Core REST APIs - please choose "Standard Authentication method" or provide the Auth ID.'; | ||
} | ||
headers = getAdvancedAuthMethodHeaders(key, auth_id, 'application/json') | ||
} | ||
var res = http( | ||
requestUrl, | ||
{ | ||
Method: method, | ||
Headers: { | ||
'Accept': ['application/json'], | ||
'content-type': ['application/json'], | ||
'authorization': key, | ||
'x-xdr-auth-id': auth_id | ||
}, | ||
Headers: headers, | ||
Body: body, | ||
SaveToFile: raw | ||
}, | ||
|
@@ -90,7 +147,7 @@ var sendRequest = function(method, uri, body, raw) { | |
); | ||
|
||
if (res.StatusCode < 200 || res.StatusCode >= 300) { | ||
throw 'Demisto REST APIs - Request Failed.\nStatus code: ' + res.StatusCode + '.\nBody: ' + JSON.stringify(res) + '.'; | ||
throw 'Core REST APIs - Request Failed.\nStatus code: ' + res.StatusCode + '.\nBody: ' + JSON.stringify(res) + '.'; | ||
} | ||
if (raw) { | ||
return res; | ||
|
@@ -104,12 +161,40 @@ var sendRequest = function(method, uri, body, raw) { | |
} | ||
return {response: response}; | ||
} catch (ex) { | ||
throw 'Demisto REST APIs - Error parsing response - ' + ex + '\nBody:' + res.Body; | ||
throw 'Core REST APIs - Error parsing response - ' + ex + '\nBody:' + res.Body; | ||
} | ||
} | ||
}; | ||
|
||
var deleteIncidents = function(ids_to_delete) { | ||
function reduce_one_entry(data, keep_fields) { | ||
var new_d = {}; | ||
for (var field_index = 0; field_index < keep_fields.length; field_index += 1) { | ||
var field = keep_fields[field_index]; | ||
if (data[field]) { | ||
new_d[field] = data[field]; | ||
} | ||
} | ||
return new_d; | ||
} | ||
|
||
function reduce_data(data, fields_to_keep) { | ||
if (data instanceof Array) { | ||
var new_data = []; | ||
for (var data_index = 0; data_index < data.length; data_index += 1) { | ||
var d = data[data_index]; | ||
new_data.push(reduce_one_entry(d, fields_to_keep)); | ||
} | ||
return new_data; | ||
} | ||
else { | ||
if (data.constructor == Object) { | ||
return [reduce_one_entry(data, fields_to_keep)]; | ||
} | ||
} | ||
return data; | ||
} | ||
|
||
var deleteIncidents = function(ids_to_delete, fields_to_keep) { | ||
Comment on lines
-112
to
+197
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was taken from DemistoRestAPI integration. |
||
var body = { | ||
ids: ids_to_delete, | ||
all: false, | ||
|
@@ -121,8 +206,11 @@ var deleteIncidents = function(ids_to_delete) { | |
throw res[0].Contents; | ||
} | ||
|
||
var response = res['response'] | ||
var md = tableToMarkdown('Demisto delete incidents', response, ['data', 'total', "notUpdated"]); | ||
var response = res['response']; | ||
if (fields_to_keep && (fields_to_keep != "all")) { | ||
response['data'] = reduce_data(response['data'], fields_to_keep); | ||
} | ||
var md = tableToMarkdown('Core delete incidents', response, ['data', 'total', "notUpdated"]); | ||
|
||
return { | ||
ContentsFormat: formats.json, | ||
|
@@ -132,6 +220,83 @@ var deleteIncidents = function(ids_to_delete) { | |
}; | ||
}; | ||
|
||
var installPack = function(pack_url, entry_id, skip_verify, skip_validation){ | ||
let file_path; | ||
if (entry_id){ | ||
file_path = entry_id; | ||
} | ||
else{ | ||
// download pack zip file | ||
var res = http( | ||
pack_url, | ||
{ | ||
Method: 'GET', | ||
Headers: {}, | ||
SaveToFile: true | ||
}); | ||
|
||
if (res.StatusCode < 200 || res.StatusCode >= 300) { | ||
throw 'Core REST APIs - Failed to download pack file from ' + pack_url; | ||
} | ||
file_path = res.Path; | ||
} | ||
|
||
let upload_url = 'contentpacks/installed/upload?' | ||
|
||
// set the skipVerify parameter | ||
if(isDemistoVersionGE('6.5.0')){ | ||
if (skip_verify && skip_verify === 'false') { | ||
upload_url+='skipVerify=false' | ||
}else{ | ||
upload_url+='skipVerify=true' | ||
} | ||
} | ||
|
||
// set the skipValidation parameter | ||
if(isDemistoVersionGE('6.6.0')){ | ||
if (skip_validation && skip_validation === 'false') { | ||
upload_url+='&skipValidation=false' | ||
}else{ | ||
upload_url+='&skipValidation=true' | ||
} | ||
} | ||
// upload the pack | ||
sendMultipart(upload_url, file_path,'{}'); | ||
}; | ||
|
||
var installPacks = function(packs_to_install, file_url, entry_id, skip_verify, skip_validation) { | ||
if ((!packs_to_install) && (!file_url) && (!entry_id)) { | ||
throw 'Either packs_to_install, file_url or entry_id argument must be provided.'; | ||
} | ||
else if (file_url) { | ||
installPack(file_url, undefined, skip_verify, skip_validation) | ||
logDebug('Pack installed successfully from ' + file_url) | ||
return 'The pack installed successfully from the file ' + file_url | ||
} | ||
else if (entry_id) { | ||
installPack(undefined, entry_id, skip_verify, skip_validation) | ||
logDebug('The pack installed successfully from the file.') | ||
return 'The pack installed successfully from the file.' | ||
} | ||
else{ | ||
let installed_packs = [] | ||
let packs = JSON.parse(packs_to_install); | ||
|
||
for (let pack_index = 0; pack_index < packs.length; pack_index += 1) { | ||
let pack = packs[pack_index]; | ||
let pack_id = Object.keys(pack)[0] | ||
let pack_version = pack[pack_id] | ||
|
||
let pack_url = '{0}{1}/{2}/{3}.zip'.format(marketplace_url,pack_id,pack_version,pack_id) | ||
installPack(pack_url, undefined, skip_verify, skip_validation) | ||
logDebug(pack_id + ' pack installed successfully') | ||
installed_packs.push(pack_id) | ||
} | ||
|
||
return 'The following packs installed successfully: ' + installed_packs.join(", ") | ||
} | ||
}; | ||
|
||
switch (command) { | ||
case 'test-module': | ||
sendRequest('GET','user'); | ||
|
@@ -174,7 +339,11 @@ switch (command) { | |
case 'demisto-delete-incidents': | ||
case 'core-delete-incidents': | ||
var ids = argToList(args.ids); | ||
return deleteIncidents(ids); | ||
var fields = argToList(args.fields); | ||
return deleteIncidents(ids, fields); | ||
case 'demisto-api-install-packs': | ||
case 'core-api-install-packs': | ||
return installPacks(args.packs_to_install, args.file_url, args.entry_id, args.skip_verify, args.skip_validation); | ||
default: | ||
throw 'Core REST APIs - unknown command'; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this url should differ if the integration running in XSIAM or XSOAR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no way to distinguish between XSIAM and XSOAR. It should be added manually by the customer.