VirusTotal Upload File - node.js

I am following the documentation in https://developers.virustotal.com/v3.0/reference#file trying to analyze files through VirusTotal API using the endpoint https://www.virustotal.com/api/v3/files but it keeps throwing the following error:
{"error": {
"message": "Received file with an empty filename. Please post with filename.",
"code": "BadRequestError"\n
}
}
The code I'm using is really simple, and I have change it adding more keys in the data object like: "name", filename", and so on but seems nothing is working:
class VirusTotal {
constructor(){
this.APIKEY = CONF.virus_apikey;
}
async checkFile(path){
let url = 'https://www.virustotal.com/api/v3/files';
let header = {'x-apikey': this.APIKEY};
let data = {file:path, name: path.split('/')[path.split('/').length -1], filename: 'asdasd'};
REQUEST(url, ['post'], data, (err, res, status, headers) => {
console.log(err, res, status, headers);
}, null, header);
}
}
Then I tried to do it through the GUI they have to test, but didn't work either, throwing the same error.

To upload a file to their API it reaquires a to read the file, pass it as a Buffer and stringify it.
I got the next solution:
async analyzeFile(path, fileType){
return new Promise((resolve, reject) => {
let file = require('fs').readFileSync(path);
const Buffer = require('buffer').Buffer;
if (typeof input=="string") file = Buffer.from(file, 'utf8')
if (Buffer.isBuffer(file)) file = file;
file = JSON.stringify(file);
const request = require('request');
request({
url: 'https://www.virustotal.com/api/v3/files',
method: 'POST',
headers: {'x-apikey': this.APIKEY},
formData: {file: {value: file, options: {filename: path.split('/')[path.split('/').length -1], filetype: fileType}}},
}, async (err, res) => {
!err ? resolve(res) : reject(err);
});
});
}
As well, per VirusTotal staff the GUI they has a bug, therefore it doesn't work.

Related

I've got an issue with ReactJS to download a file from Nodejs

I need some help on an issue. I try to download a file on my client ReactJs from my server Nodejs.
I've got into my server.js :
router.route("/download/:filesaveas").get(function(req, res) {
const fileLocation = "public/files/" + req.params.filesaveas;
const file = req.params.filesaveas;
res.download(fileLocation, file, (err) => {
if (err) console.log(err);
});
When I try to download directly from the server http://localhost:4000/cww/download/testfile.pdf, the download works, I don't have any error and the file is not corrupted.
On my client side, I've got a function downloadFile which is called by a button "onclick" action.
import download from 'downloadjs'
downloadFile = (filetodownload) => {
axios.get('http://localhost:4000/cww/download/'+filetodownload)
.then(res => {
var filename = "testfile.pdf"
download(res.data, scriptname, "text/plain");
});
}
When I click on the button. Something is downloaded but the file seems to be corrupted. Impossible to open... I think, I've got a problem with the response data from the server.
By doing a console.log(res.data), I can see a part of my content PDF but with some strange
characters (like encoding) but impossible to have a correct file downloaded.
Please thanks for your help.
if you want easiest option would be to open a new tab with that file's address which works only if route is public.
const newTab = false;
window.open('http://localhost:4000/cww/download/testfile.pdf',newTab ? '' : '_self' );
but you can do it without touching the file encoding or even use axios for this:
onClick() {
const fileName = 'testfile.pdf';
fetch('http://localhost:4000/cww/download/testfile.pdf', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
// Security Headers if needed
},
body: undefined,
})
.then((data) => {
return data.blob();
})
.then((data) => {
if (data.size === 0) {
throw new Error('File not found');
}
const fileURL = URL.createObjectURL(data);
const downloadLink = document.createElement('a');
downloadLink.href = fileURL;
downloadLink.download = fileName;
downloadLink.click();
})
.catch((err) => {
console.log(err);
// Error Action
});
}
Backend will simply stream the file to user.
I don't know much about res.download (might be better solution)
import * as fs from 'fs';
export async function getFile(request, response) {
const fileStream = fs.createReadStream('yourFileAddress', {});
fileStream.on('error', (err) => {
console.log(err.message);
response.status(404).send();
fileStream.removeAllListeners();
});
fileStream.on('end', () => {
console.log('Streamed successfully to user');
fileStream.removeAllListeners();
});
// finally starting the stream
fileStream.pipe(response);
}
Thanks for your help ! I've just found my error !
I forgot to add ReponseType: blob and now it works perfectly ;-)
downloadFile = (filetodownload) => {
axios.get('http://localhost:4000/cww/download/'+filetodownload, {responseType: 'blob'})
.then(res => {
var filename = "testfile.pdf"
download(res.data, scriptname, "text/plain");
});
}

NODE GridFStorage with additional data from request doesn't always show up

Hey guys i'm confronting a very strange behaviour with GridFs while i try to upload a file:
So i send with formdata my file which i want to upload and a code which will be set in metadata in files,
the files are saved correctly and the originalname field is always added to the metadata but the code field which is a req.body paramater has a very strange behaviour.
files.ts
uploadFileFormSubmit(event) {
const formData = new FormData();
formData.append('file', event.target.files.item(0));
formData.append('code', this.courseCode);
this.fileService.uploadFile(formData).subscribe(res => ....
fileService.ts
uploadFile(data): Observable<GeneralResponse> {
return this.http.post<GeneralResponse>('/files/uploadFile', data);
}
here is the backend part:
files.js (back-end)
const storage = new GridFsStorage({
url: dbUrl,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
console.log(req.body)
const code = JSON.parse(JSON.stringify(req.body));
console.log(code)
const fileInfo = {
filename: filename,
metadata: {
originalname: file.originalname,
materialCode: code.code
},
bucketName: 'files'
};
resolve(fileInfo);
});
});
}
});
As you can see i parse the req.body in order to get my property (i found it here this solution because the req.body was [Object: null prototype] { code: 'myCode'} )
And for some files this code data is passed but not always.
note that there are 2 console.logs(before and after JSON.parse()
the first object null is an excel file,
the second is a pdf
the third is jpg file
the fourth is a png file
maybe it's something with the extensions but i cannot imagine why the req.body sometimes gets parsed
and the code gets into metadata but other times not :/
So what can cause this behaviour? thanks for help in advance :D

Getting the file in busboy to upload to firebase storage

I am using busboy in node to upload a file to the firebase storage.
But everytme i send the post request with a file, it says..
{
"e": {
"code_": "storage/invalid-argument",
"message_": "Firebase Storage: Invalid argument in `put` at index 0: Expected Blob or File.",
"serverResponse_": null,
"name_": "FirebaseError"
}
}
I can't figure out how do i send the file using busboy..
My code snippet is..
export const uploadAvatar = (req: any, res: any) => {
const busboy = new BusBoy({ headers: req.headers });
let filepath: any;
let imgToBeUploaded: any;
busboy.on(
'file',
(
fieldname: string,
file: any,
filename: string,
encoding: string,
mimetype: string
) => {
if (mimetype !== 'image/jpeg' && mimetype !== 'image/png') {
res.status(400).json({
error: 'Wrong image format',
});
}
const imgExt: string = filename.split('.')[
filename.split('.').length - 1
];
const imgName = `avatar_${req.user.userName}.${imgExt}`;
filepath = path.join(os.tmpdir(), imgName);
// modifiedUrl = `avatar_${req.user.userName}_200x200.${imgExt}`;
file.pipe(fs.createWriteStream(filepath));
imgToBeUploaded = { filepath, mimetype, file };
}
);
busboy.on('finish', async () => {
const storageRef = storage.ref();
try {
const uploadTask = await storageRef.put(imgToBeUploaded.filepath);
console.log(`UploadTask : ${uploadTask}`);
return res.json('File uploaded');
} catch (e) {
return res.status(400).json({ e });
}
});
busboy.end(req.rawBody);
};
The console.log of 'file' returns the location in tempdir, where the file is stored...
Please help me figure out how do i get busboy to return the file, which i can pass to the storageRef.put() 's argument.
For anyone who's here looking for an answer... I've had this problem for over a week now. Kept getting Error: TypeError: Cannot read properties of undefined (reading 'byteLength'). Try doing
storageRef.put(fs.readFileSync(imgToBeUploaded.filepath))
It will actually read the data into a buffer from the temporary file on your local computer and send it on to firestore.
Also it might help to console.log your fs.statSync(imgToBeUploaded.filepath) to make sure the file is actually written. Check the size to make sure it's in the expected range for your image.

how to loop the url in options in nodejs

var request = require('request');
var options = {
'method': 'GET',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=1',//To get all the users data from the repos
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=2',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=3',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=4',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=5',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=6',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=7',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=8',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=9',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=10',
'url': 'https://api.github.com/orgs/organizationName/repos?per_page=100&page=11',
'headers': {
'Accept': 'application/vnd.github.mercy-preview+json',//to get topics of the repos
'Authorization': 'Bxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'User-Agent' : 'sxxxxxxxxxxxxx'
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
In this above code i want to loop the urls continuously until the end of the page
if not anyone have the idea of using pagination in this help me out
cYou cannot have multiple attributes for one object key. You have to call every url individually. I tried to solve this using asyncronous code, because looping with callback functions is confusing and dangerous with regard to the call stack.
const request = require('request');
// configuration for the url generation
const perPages = 100;
const startPage = 1;
const endPage = 11;
const url = 'https://api.github.com/orgs/organizationName/repos?per_page=%perPages%&page=%page%';
// define a asyncronous call for one url
async function callOneUrl(url) {
// local options for each url
const options = {
method: 'GET',
url: url,
headers: {
Accept: 'application/vnd.github.mercy-preview+json',
Authorization: 'Bxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'User-Agent': 'sxxxxxxxxxxxxx'
}
}
return new Promise((resolve, reject) => {
request(options, function (error, response) {
if (error) return reject(error);
resolve(response);
});
});
}
// call each url with a for loop
(async () => {
for (let i = startPage; i <= endPage; i++) {
// using the await statement to get the resolved value of the Promise instance or catch the error
try {
var response = await callOneUrl(url.replace('%perPages%', perPages).replace('%page%', i));
// handle response here
console.log(response.body);
} catch (error) {
// handle errors here
throw new Error(error);
}
}
})()
const request = require('request-promise');
const urls = ["http://www.google.com", "http://www.example.com"];
const promises = urls.map(url => request(url));
Promise.all(promises).then((data) => {
// data = [promise1,promise2]
});
Apart from above you can also use async.eachseries or async.parallel etc..
You can download a list of repos with a do...while loop. We'll set a maximum number of pages to download and exit when we reach either this or the last page.
I would suggest using the request-promise-native package to allow us to use the very nice async-await syntax.
Now, I've given the example of downloading repos for the mongodb org. You can easily replace with whatever one you wish.
I would also note that the request library is now deprecated, we can use it of course, but we must consider replacing in the future.
We now also log the repo information and save it to the output file.
const rp = require("request-promise-native");
const fs = require("fs");
async function downloadRepoInformation(org, outputFile) {
let repoList = [];
let page = 0;
const resultsPerPage = 20;
const maxPages = 10;
const uri = `https://api.github.com/orgs/${org}/repos`;
do {
try {
let response = await rp.get({ uri, json: true, qs: { per_page: resultsPerPage, page: ++page }, headers: {"User-Agent" : "request"} });
console.log(`downloadRepoInformation: Downloaded page: ${page}, repos: ${response.length}...`);
repoList = repoList.concat(response);
console.log("downloadRepoInformation: response", JSON.stringify(response, null, 2));
console.log("downloadRepoInformation: repoList.length:", repoList.length);
if (response.length < resultsPerPage) {
console.log(`downloadRepoInformation: Last page reached: exiting loop...`);
break;
}
} catch (error) {
console.error(`downloadRepoInformation: An error occurred:`, error);
break;
}
} while (page <= maxPages)
console.log("downloadRepoInformation: download complete: repoList.length:", repoList.length)
console.log("downloadRepoInformation: Saving to file:", outputFile);
fs.writeFileSync(outputFile, JSON.stringify(repoList, null, 4));
}
downloadRepoInformation("mongodb", "./repolist.json");

Getting ERROR: uncaughtException: source.on is not a function, when using request and multiparty for multipart/form-data

I am trying to send data from my node application to a 3rd party HTTP endpoint.
I am parsing the data on the request object coming from the client using the multiparty module and sending the data through the request module. I am getting the error
error: uncaughtException: source.on is not a function
var request = require('request');
const multiparty = require('multiparty');
function addAttachment(req, res) {
let form = new multiparty.Form();
let parsedFile = {};
const formData = {};
form.parse(req, function(err, fields, files){
Object.keys(fields).forEach(function(name) {
formData[name] = fields[name][0];
});
Object.keys(files).forEach(function(name) {
logger.debug(name);
parsedFile[name] = files[name][0];
});
formData.uploadFile = parsedFile.uploadFile;
logger.debug('formData ', formData);
reqOptions.url = imageURL;
reqOptions.formData = formData;
logger.debug('REQ_OPTIONS ', reqOptions);
request.post(reqOptions, function (err, response, body) {
if (err) {
logger.warn(req, ' Error sending attachment', err);
res.status(400);
res.json({ "msg": "Error sending attachment" });
} else {
res.status(201);
logger.debug('BODY ', body);
res.send(body);
}
});
});
}
The reqOptions obj contains the headers, url, auth obj, we then add the form data to it.
When I log the form data it looks to be in the correct format
{
"meta": {
"prop1": "xxxxxx",
"prop2": "xxxxxxxxxxxxx",
"uploadFile": {
"fieldName": "uploadFile",
"originalFilename": "test.PNG",
"path": "/tmp/W1IppPiK04JpkPrnZWEhzkmV.PNG",
"headers": {
"content-disposition": "form-data; name=\"uploadFile\"; filename=\"test.PNG\"",
"content-type": "image/png"
},
"size": 42786
}
}
}
This error is probably one of the best examples how error message can be perfectly misleading. Therefore. it's very frustrating to do RCA of the issue:
ERROR: uncaught Exception: source.on is not a function
Actually there is nothing about any function here. In my case, I spent hours scratching my head and finally only to find it is JSON under another JSON which was causing this error:
let subJson =
{
field1: "value1",
field2: "value2"
}
let myJson =
{
field1: "value1",
field2: "value2",
field3: subJson
}
createFormData(myJson);
This is it! When you call createFormData with myJson as parameter, you will see exception source.on is not a function! And we keep thinking where is that function?
Solution is JSON.stringify
field3: JSON.stringify(subJson)
Will solve this issue.
javascript!
So after some hair pulling and digging around, I was able to post form data to the external API. I decide to change the node modules I was using to connect-multiparty. Connect will parse the request headers and decode the post-form-data allowing you to access the data from the req obj E.G req.body now have the added properties and req.files has uploaded files.
const multipart = require('connect-multiparty');
const multipartMiddleware = multipart();
Then add the multipartMiddleware to the route.
app.post('/api/addAttachment' multipartMiddleware, MyController.addAttachment);
Then in my controller file I changed the code to use connect-multipart.
const fs = require('fs');
var request = require('request');
function addAttachment(req, res) {
const TMP = '/tmp';
let formData = {};
Object.keys(req.body).forEach((propName) =>{
if (typeof propName === 'string') {
logger.debug(propName, ' is a string');
formData[propName] = req.body[propName];
} else {
logger.debug(propName, ' is not a string')
}
});
//The files get added to the tmp folder on the files system,
//So we create a stream to read from tmp folder,
//at the end end we need to delete the file
formData['uploadFile'] = fs.createReadStream(req.files.uploadFile.path);
logger.debug('FORM DATA ', formData, '\n');
reqOptions.url = imageUrl;
reqOptions.headers = {'Content-Type': 'multipart/form-data','Accept': 'application/json'};
reqOptions.formData = formData;
logger.debug('REQ_OPTIONS ', reqOptions, '\n');
request.post(reqOptions, function (err, response, body) {
if (err) {
removeFiles(TMP);
logger.warn(req, ' Error sending attachment', err);
res.status(400);
res.json({"msg": "Error sending attachment"});
} else {
removeFiles(TMP);
res.status(201);
logger.debug('BODY ', body);
res.send(body);
}
});
}

Resources