Skip to content

Commit

Permalink
Xsoar file management (demisto#26455)
Browse files Browse the repository at this point in the history
* commonserver.js

* working

* ok

* fileDeleteAttachmentCommand

* read me

* removing examples file

* removing changes

* rl update

* small fixes

* removing extra space

* RL

* remove RL

* adding version

* RL base

* commit

* temp

* coreApiFileCheckCommand fix

* fixing fileDeleteCommand

* fileUploadCommand fix

* rl

* Bump pack from version Base to 1.32.5.

* after conflicts

* Rl

* xsoar concate bug fix

* docstring

* undo changes in unrelevant files

* removing _mm

* Bump pack from version Base to 1.32.6.

* Bump pack from version Base to 1.32.7.

* val changes

* removing notes

* small fixes

* cr fixes

* fileUploadCommand fix

* small update

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/ReleaseNotes/1_3_26.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/ReleaseNotes/1_3_26.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/ReleaseNotes/1_3_26.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/ReleaseNotes/1_3_26.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/ReleaseNotes/1_3_26.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/README.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/README.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/README.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/README.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/README.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/README.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/README.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* Update Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/README.md

Co-authored-by: ShirleyDenkberg <[email protected]>

* specifying what the FileResult function does

* RL

* known word

* ignore word

* adding to read me

* Update Packs/Base/ReleaseNotes/1_32_7.md

Co-authored-by: Shelly Tzohar <[email protected]>

* removed from read me old demisto command

* adding to ignore

* ignore

* Bump pack from version Base to 1.32.8.

* m

* Bump pack from version Base to 1.32.9.

* Bump pack from version Base to 1.32.10.

* Bump pack from version Base to 1.32.11.

* Bump pack from version Base to 1.32.12.

* demo fixes

* Bump pack from version Base to 1.32.15.

---------

Co-authored-by: Content Bot <[email protected]>
Co-authored-by: ShirleyDenkberg <[email protected]>
Co-authored-by: Shelly Tzohar <[email protected]>
  • Loading branch information
4 people authored and xsoar-bot committed Jul 26, 2023
1 parent 321ed19 commit b86b417
Show file tree
Hide file tree
Showing 10 changed files with 427 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Packs/Base/.pack-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ refang
indicatorssearcher
FeedIndicatorType
str
FileResult
JavaScript

[tests_require_network]
CommonServerPython
Expand Down
5 changes: 5 additions & 0 deletions Packs/Base/ReleaseNotes/1_32_15.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

#### Scripts

##### CommonServer
Added a **FileResult** function to create a file from the given data in *JavaScript*.
21 changes: 21 additions & 0 deletions Packs/Base/Scripts/CommonServer/CommonServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2103,3 +2103,24 @@ function mergeContextLists(newItems, oldItems, objectKey) {
var newItemsByKey = newItems.reduce(toMapByKey, {});
return Object.values(Object.assign(oldItemsByKey, newItemsByKey)).filter(function() {return !e['remove']});
}

/**
* Creates a file from the given data.
* @param {Object} entryType - The entry type from entryTypes.
* @param {string} file_name - The name of the file.
* @param {string} file_content - The content of the file.
* @param {boolean} human_readable - (Optional) if human_readable isn't passed the human readable would be the name of the file.
* @returns {dict} A Demisto war room entry.
*/
function fileResult(file_name, file_content,entryType=null, human_readable_optional=null){

fileEntryId = saveFile(file_content);
HumanReadable = (!(human_readable_optional)) ? `${file_name} uploaded with entryID: ${fileEntryId}.` : human_readable_optional;
return {
Type: entryType,
FileID: fileEntryId,
File: file_name,
Contents: file_content,
HumanReadable: HumanReadable
};
};
2 changes: 1 addition & 1 deletion Packs/Base/pack_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Base",
"description": "The base pack for Cortex XSOAR.",
"support": "xsoar",
"currentVersion": "1.32.14",
"currentVersion": "1.32.15",
"author": "Cortex XSOAR",
"serverMinVersion": "6.0.0",
"url": "https://www.paloaltonetworks.com/cortex",
Expand Down
2 changes: 1 addition & 1 deletion Packs/DemistoRESTAPI/.pack-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ ignore=RM106
ignore=SC106

[file:CoreRESTAPI.yml]
ignore=IN139
ignore=IN139,RM110
260 changes: 259 additions & 1 deletion Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ sendMultipart = function (uri, entryID, body) {
};

var sendRequest = function(method, uri, body, raw) {
var requestUrl = getRequestURL(uri)
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.';
Expand Down Expand Up @@ -313,6 +313,256 @@ var installPacks = function(packs_to_install, file_url, entry_id, skip_verify, s
}
};


/* helper functions */

/**
* deletes an entry by entryID by the key_to_delete
Arguments:
@param {String} file_content -- content of the file to upload
@param {String} file_name -- name of the file in the dest incident
@param {String} key_to_delete -- the name of the key to delete
@param {String} incident_id -- the incident id
Returns:
CommandResults
"""
*/
var uploadFile= function(incident_id, file_content, file_name) {
var body = {
file:
{
value: [file_content],
options: {
filename: [file_name],
contentType: 'multipart/form-data'
}
},
};
var res = httpMultipart(`/entry/upload/${incident_id}`,file_content ,body);
if (isError(res[0])) {
throw res[0].Contents;
}
return res;
};

/**
* deletes an entry by entryID by the key_to_delete
Arguments:
@param {String} key_to_delete -- the name of the key to delete
@param {String} incident_id -- the incident id
Returns:
CommandResults
"""
*/
var deleteContextRequest = function (incident_id, key_to_delete) {
var body = JSON.stringify({
"args": null,
"id": "",
"investigationId": `${incident_id}`,
"data": `!DeleteContext key=${key_to_delete}\n`,
"markdown": false,
"version": 0
});
return sendRequest('POST', '/entry', body);
};


/**
* deletes a file by entryID
Arguments:
@param {String} delete_artifact -- in order to delete the artifact
@param {String} entry_id -- entry ID of the file
Returns:
CommandResults
"""
*/
var deleteFileRequest = function (entry_id, delete_artifact = true) {
const body_content = JSON.stringify({
id: entry_id,
deleteArtifact: delete_artifact});

return sendRequest( 'POST', '/entry/delete/v2', body_content);
};


/**
* Sends http request to delete attachment
Arguments:
@param {String} incident_id -- incident id to upload the file to
@param {String} file_path -- the file path to delete
@param {String} field_name -- Name of the field (type attachment) you want to remove the attachment
Returns:
Results
"""
*/
var deleteAttachmentRequest=function(incident_id, attachment_path, field_name = 'attachment') {
body = JSON.stringify({
fieldName: field_name,
files: {
[attachment_path]: {
path: attachment_path
}
},
originalAttachments: [
{
path: attachment_path
}
]
});
try{
return sendRequest('POST', `/incident/remove/${incident_id}`, body);
}
catch (e) {
throw new Error(`File already deleted or not found.\n${e}`);
}
};

/**
* Upload a new file
Arguments:
@param {String} incident_id -- incident id to upload the file to
@param {String} file_content -- content of the file to upload
@param {String} file_name -- name of the file in the dest incident
@param {String} entryID -- entry ID of the file
Returns:
CommandResults -- Readable output
Note:
You can give either the entryID or file_name.
"""
*/
var fileUploadCommand = function(incident_id, file_content, file_name, entryID ) {
incident_id = (incident_id === 'undefined')? investigation.id: incident_id;
if (incident_id!=investigation.id){
log(`Note that the file would be uploaded to ${incident_id} from incident ${investigation.id}`);
}
if ((!file_name) && (!entryID)) {
throw 'Either file_name or entry_id argument must be provided.';
}
var fileId = '';
if ((!entryID)) {
response = uploadFile(incident_id, file_content, file_name);
fileId = saveFile(file_content);
} else {
if (file_name === undefined) {
file_name = dq(invContext, `File(val.EntryID == ${entryID}).Name`);
}
if (Array.isArray(file_name)) {
if (file_name.length > 0) {
file_name = file_name[0];
} else {
file_name = undefined;
}
}
response_multi= sendMultipart(`/incident/upload/${incident_id}`,entryID,'{}');
return `The file ${entryID} uploaded successfully to incident ${incident_id}. `;
}
var md = `File ${file_name} uploaded successfully to incident ${incident_id}.`;
fileId = file_name ? fileId : entryID;
return {
Type: entryTypes.file,
FileID: fileId,
File: file_name,
Contents: file_content,
HumanReadable: md
};
};



/**
* Deletes a specific file.
Arguments:
@param {String} entryId -- entry ID of the file
Returns:
Message that the file was deleted successfully + entry_id
"""
*/
// getting the context data
var fileDeleteCommand = function(EntryID) {
files = invContext['File'];
if (!files){
throw new Error(`Files not found.`);
}
files = (invContext['File'] instanceof Array)? invContext['File']:[invContext['File']];
if (files[0]=='undefined'){
throw new Error(`Files not found.`);

}
var edit_content_data_files = []
var not_found = true
for (var i = 0 ;i <=Object.keys(files).length - 1; i++) {
if (files[i]['EntryID'] != EntryID) {
edit_content_data_files.push(files[i]);
}
else{
not_found= false
}

}
if(not_found){
throw new Error(`File already deleted or not found.`);
}
deleteContextRequest(investigation.id, 'File');
deleteFileRequest(EntryID);
let context = {
'File(val.MD5==obj.MD5)': createContext(edit_content_data_files)
};
return {Type: entryTypes.note,
Contents: '',
ContentsType: formats.json,
EntryContext: context,
HumanReadable: `File ${EntryID} was deleted successfully.`};


}


/**
This command checks if the file is existing.
Arguments:
@param {String} EntryID -- entry ID of the file
Returns:
Dictionary with EntryID as key and boolean if the file exists as value.
*/
function coreApiFileCheckCommand(EntryID) {
files = invContext['File']instanceof Array? invContext['File']:[invContext['File']];
var file_found = false;
var human_readable = `File ${EntryID} isn't exists`;
if (typeof files['0'] !== 'undefined') {
for (var i = 0 ;i <=Object.keys(files).length - 1; i++) {
if (files[i]['EntryID'] == EntryID) {
file_found= true ;
human_readable = `File ${EntryID} exists`;
}
}
}
return {
Type: entryTypes.note,
Contents: {[EntryID]:file_found},
HumanReadable: human_readable,
EntryContext: {[`IsFileExists(val.${EntryID}==${EntryID})`]:{[EntryID]:file_found}}
};


};

/**
This command deletes attachment from an incident.
Arguments:
@param {String} incident_id -- incident id to delete the file from
@param {String} attachment_path -- the file path
@param {String} field_name -- Name of the field (type attachment) you want to remove the attachment
Returns:
Show a message that the file was deleted successfully
*/
var fileDeleteAttachmentCommand = function (attachment_path, incident_id, field_name){
incident_id = (incident_id=='undefined')? investigation.id: incident_id;
deleteAttachmentRequest(incident_id, attachment_path, field_name);
return `Attachment ${attachment_path} deleted `;
};



switch (command) {
case 'test-module':
sendRequest('GET','user');
Expand Down Expand Up @@ -360,6 +610,14 @@ switch (command) {
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);
case 'core-api-file-upload':
return fileUploadCommand(args.incident_id, args.file_content, args.file_name, args.entry_id)
case 'core-api-file-delete':
return fileDeleteCommand(args.entry_id);
case 'core-api-file-attachment-delete':
return fileDeleteAttachmentCommand(args.file_path, args.incident_id, args.field_name);
case 'core-api-file-check':
return coreApiFileCheckCommand(args.entry_id);
default:
throw 'Core REST APIs - unknown command';
}
47 changes: 46 additions & 1 deletion Packs/DemistoRESTAPI/Integrations/CoreRESTAPI/CoreRESTAPI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,52 @@ script:
predefined:
- 'true'
- 'false'
description: Upload packs to Core server from url or the marketplace.
description: Upload packs to the core server from the URL or marketplace.
- name: core-api-file-upload
arguments:
- name: incident_id
required: false
description: The incident's ID.
- name: file_name
required: false
description: "The new file's name."
- name: file_content
description: "The new file's content."
- name: entry_id
description: "The War Room entry ID of the pack zip file."
required: false
isArray: false
description: Upload to the incident a file that the user provided according to the entry_id or the content of the file.
- name: core-api-file-delete
arguments:
- name: entry_id
description: "The War Room entry ID of the file."
required: true
isArray: false
description: Delete a file from Cortex XSOAR by entry_id.
- name: core-api-file-attachment-delete
arguments:
- name: incident_id
required: true
description: The incident's ID.
- name: file_path
required: true
description: "The file's path."
- name: field_name
defaultValue: 'attachment'
required: false
description: "Name of the field (type attachment) from which to remove the attachment."
description: Delete the attachment from the incident and from the Cortex XSOAR server.
- name: core-api-file-check
arguments:
- name: entry_id
description: "The War Room entry ID of the file."
required: true
isArray: false
description: Check if the file exists in Cortex XSOAR by entry_id.
outputs:
- description: Dictionary with EntryID as the key and boolean if the file exists as a value.
contextPath: IsFileExists
runonce: false
tests:
- No tests
Expand Down
Loading

0 comments on commit b86b417

Please sign in to comment.