Skip to content
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 31 commits into from Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
077a34c
fix
dansterenson Jan 4, 2023
7c7a6d8
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 8, 2023
2da5e90
yml fixes
dansterenson Jan 8, 2023
bc89da7
more fixes
dansterenson Jan 9, 2023
475f8bc
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 11, 2023
81e1fcd
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 12, 2023
a86e49e
add support to Standard and Advanced
dansterenson Jan 12, 2023
664bcb3
fix readme and yml
dansterenson Jan 15, 2023
aeca4de
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 15, 2023
842582e
update RN
dansterenson Jan 15, 2023
9316e9a
Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml
dansterenson Jan 15, 2023
284c1a0
support empty auth_method
dansterenson Jan 15, 2023
ddf0be0
Merge branch 'XSOAR-NG-update-Demisto-Rest-API' of github.com:demisto…
dansterenson Jan 15, 2023
de4b010
remove marketplaces field
dansterenson Jan 15, 2023
9f459f8
auth ID handle and deprecate DemistoRestApi
dansterenson Jan 17, 2023
c4571e9
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 17, 2023
db72a93
update RN
dansterenson Jan 17, 2023
16e3186
fix validation errors
dansterenson Jan 17, 2023
357c620
remove additional info
dansterenson Jan 17, 2023
cd3a1fb
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 17, 2023
8c893e5
more fixes
dansterenson Jan 18, 2023
b8ddd3a
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 18, 2023
8b3f829
more fixes
dansterenson Jan 18, 2023
b534504
Add core-api-install-packs
dansterenson Jan 19, 2023
886491a
Fix names
dansterenson Jan 19, 2023
9e564de
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 19, 2023
a249e15
deprecate in description
dansterenson Jan 19, 2023
062f8b2
deprecate in description
dansterenson Jan 19, 2023
edb9994
update RN
dansterenson Jan 19, 2023
4451e23
more fixes
dansterenson Jan 19, 2023
7b1c731
Merge branch 'master' into XSOAR-NG-update-Demisto-Rest-API
dansterenson Jan 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
241 changes: 205 additions & 36 deletions Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.js
Expand Up @@ -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/'
Copy link
Contributor

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

Copy link
Contributor Author

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.


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,
Expand All @@ -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;
Expand All @@ -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
},
Expand All @@ -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;
Expand All @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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,
Expand All @@ -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,
Expand All @@ -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');
Expand Down Expand Up @@ -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';
}