In Node.JS, when I do a POST request, what is the maximum size? [duplicate] - http

I created an upload script in node.js using express/formidable. It basically works, but I am wondering where and when to check the uploaded file e. g. for the maximum file size or if the file´s mimetype is actually allowed.
My program looks like this:
app.post('/', function(req, res, next) {
req.form.on('progress', function(bytesReceived, bytesExpected) {
// ... do stuff
});
req.form.complete(function(err, fields, files) {
console.log('\nuploaded %s to %s', files.image.filename, files.image.path);
// ... do stuff
});
});
It seems to me that the only viable place for checking the mimetype/file size is the complete event where I can reliably use the filesystem functions to get the size of the uploaded file in /tmp/ – but that seems like a not so good idea because:
the possibly malicious/too large file is already uploaded on my server
the user experience is poor – you watch the upload progress just to be told that it didnt work afterwards
Whats the best practice for implementing this? I found quite a few examples for file uploads in node.js but none seemed to do the security checks I would need.

With help from some guys at the node IRC and the node mailing list, here is what I do:
I am using formidable to handle the file upload. Using the progress event I can check the maximum filesize like this:
form.on('progress', function(bytesReceived, bytesExpected) {
if (bytesReceived > MAX_UPLOAD_SIZE) {
console.log('### ERROR: FILE TOO LARGE');
}
});
Reliably checking the mimetype is much more difficult. The basic Idea is to use the progress event, then if enough of the file is uploaded use a file --mime-type call and check the output of that external command. Simplified it looks like this:
// contains the path of the uploaded file,
// is grabbed in the fileBegin event below
var tmpPath;
form.on('progress', function validateMimetype(bytesReceived, bytesExpected) {
var percent = (bytesReceived / bytesExpected * 100) | 0;
// pretty basic check if enough bytes of the file are written to disk,
// might be too naive if the file is small!
if (tmpPath && percent > 25) {
var child = exec('file --mime-type ' + tmpPath, function (err, stdout, stderr) {
var mimetype = stdout.substring(stdout.lastIndexOf(':') + 2, stdout.lastIndexOf('\n'));
console.log('### file CALL OUTPUT', err, stdout, stderr);
if (err || stderr) {
console.log('### ERROR: MIMETYPE COULD NOT BE DETECTED');
} else if (!ALLOWED_MIME_TYPES[mimetype]) {
console.log('### ERROR: INVALID MIMETYPE', mimetype);
} else {
console.log('### MIMETYPE VALIDATION COMPLETE');
}
});
form.removeListener('progress', validateMimetype);
}
});
form.on('fileBegin', function grabTmpPath(_, fileInfo) {
if (fileInfo.path) {
tmpPath = fileInfo.path;
form.removeListener('fileBegin', grabTmpPath);
}
});

The new version of Connect (2.x.) has this already baked into the bodyParser using the limit middleware: https://github.com/senchalabs/connect/blob/master/lib/middleware/multipart.js#L44-61
I think it's much better this way as you just kill the request when it exceeds the maximum limit instead of just stopping the formidable parser (and letting the request "go on").
More about the limit middleware: http://www.senchalabs.org/connect/limit.html

Related

Sending attachment file via http post request

I'm trying to send attachment file from youtrack to another system (in this example to trello) without the use of image url but its content
I cannot send it as image url in youtrack because my system is closed and accessible to only those that have vpn.
Problem is with reading inputStream from content of attachement in workflow. I symply have no idea how to do it and youtrack documentation havent touched it (as far as my research goes)
Code: (with truncated not important parts)
//...
exports.rule = entities.Issue.onChange({
//...
action: function(ctx) {
//...
var link = '/1/cards/' + issue['trelloIssueId'] + '/attachments';
issue.comments.added.forEach(function(comment) {
comment.attachments.forEach(function(attachment) {
var response = connection.postSync(link, {
name: attachment.name,
file: attachment.content,
mimeType: attachment.mimeType
});
//...
});
});
},
requirements: {}
});
from this I got error:
TypeError: invokeMember (forEach) on jetbrains.youtrack.workflow.sandbox.InputStreamWrapper#677a561f failed due to: Unknown identifier: forEach
How do I have to prepare content to ba abble to send it with postSync method?
It looks like you tried to iterate over issue.comments.added while the loop should be executed over issue.comments as there is no added key for an issue's comments Set as per the following documentation page suggest: https://www.jetbrains.com/help/youtrack/devportal/v1-Issue.html
Please let me know if that works.

Alamofire v4, Swift v3 Uploading Sqlite file to domain

I’m trying to upload an Sqlite database from IOS Swift 3 to my server using Alamofire 4.0, but having problems converting the sqlite file into the data type required to upload.
The majority of posts / question examples seem to default to uploading images, but I am struggling to find example of uploading sqlite or other file types (for back-up purposes)
I have searched for the basic code and found this so far which looks very reasonable (thanks to following post: Alamofire 4 upload with parameters)
let parameters = ["file_name": "swift_file.jpeg"]
Alamofire.upload(multipartFormData: { (multipartFormData) in
multipartFormData.append(UIImageJPEGRepresentation(self.photoImageView.image!, 1)!, withName: "photo_path", fileName: "swift_file.jpeg", mimeType: "image/jpeg")
for (key, value) in parameters {
multipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key)
}
}, to:"http://sample.com/upload_img.php")
{ (result) in
switch result
{
case .success(let upload, _, _):
upload.uploadProgress(closure: { (progress) in
//Print progress
})
upload.responseJSON { response in
//print response.result
}
case .failure(let encodingError):
//print encodingError.description
}
}
The part I’m struggling with is to append the sqlite file to the upload (multipartFormData.append(………..?) I’ve searched but not found any good reference posts.
Yes, i’m a newbe, but trying hard, any help would be appreciated…..
It's exactly the same as the image example except that the mime type would be application/octet-stream.
Also, you'd probably go ahead and load it directly from the fileURL rather than loading it into a Data first.
As an aside, the parameters in that example don't quite make sense, as it looks redundant with the filename provided in the upload of the image itself. So you'd use whatever parameters your web service requires, if any. If you have no additional parameters, you'd simply omit the for (key, value) { ... } loop entirely.
Finally, obviously replace the file field name with whatever field name your web service is looking for.
// any additional parameters that must be included in the request (if any)
let parameters = ["somekey": "somevalue"]
// database to be uploaded; I'm assuming it's in Documents, but perhaps you have it elsewhere, so build the URL appropriately for where the file is
let filename = "test.sqlite"
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent(filename)
// now initiate request
Alamofire.upload(multipartFormData: { multipartFormData in
multipartFormData.append(fileURL, withName: "file", fileName: filename, mimeType: "application/octet-stream")
for (key, value) in parameters {
multipartFormData.append(value.data(using: .utf8)!, withName: key)
}
}, to: urlString) { result in
switch result {
case .success(let upload, _, _):
upload
.authenticate(user: self.user, password: self.password) // only needed if you're doing server authentication
.uploadProgress { progress in
print(progress.fractionCompleted)
}
.responseJSON { response in
print("\(response.result.value)")
}
case .failure(let encodingError):
print(encodingError.localizedDescription)
}
}
Unrelated, but if you're ever unsure as to what mime type to use, you can use this routine, which will try to determine mime type from the file extension.
/// Determine mime type on the basis of extension of a file.
///
/// This requires MobileCoreServices framework.
///
/// - parameter url: The file `URL` of the local file for which we are going to determine the mime type.
///
/// - returns: Returns the mime type if successful. Returns application/octet-stream if unable to determine mime type.
func mimeType(for url: URL) -> String {
let pathExtension = url.pathExtension
if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue() {
if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
return mimetype as String
}
}
return "application/octet-stream";
}

File download from API to Meteor server and upload to S3

I am sending a request from my Meteor server to download a file via an API. I then want to upload that file to S3. I keep getting the following "NoSuchKey: The specified key does not exist." I initially thought it was maybe a problem with my AcessKey/SecretKey form AWS but after googling this for a while the only examples I could find of other people getting this error is when trying to download a file from S3.
Setting up cfs:s3
var imageStore = new FS.Store.S3("images", {
accessKeyId: "MyAcessKeyId", //required if environment variables are not set
secretAccessKey: "MySecretAcessKey", //required if environment variables are not set
bucket: "BucketName", //required
});
Images = new FS.Collection("images", {
stores: [imageStore]
});
Start file transfer from API and upload to S3
client.get_result(id, Meteor.bindEnvironment(function(err, result){ //result is the download stream and id specifies which file to download.
if (err !== null){
return;
}
var file = new FS.File(result);
Images.insert(file, function (err, fileObj) {
if (err){
console.log(err);
}
});
}));
Note: I was getting the following error so I added Meteor.bindEnvironment.
"Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment."
Node.js example from API Documentation
client.get_result(id, function(err, result){
if (err != null) {
return;
}
file.writeFile(path.join('public', path.join('results', filename)), result, 'binary');
});
What ended up fixing the problem for me was moving part of the setup to the lib folder. Although I tried several different ways I was unable to get it to execute entirely on the server. It looks like the documentation was updated recently which states everything a bit more clearly. If you follow this setup it should eliminate the error. See the section titled Client, Server, and S3 credentials
https://github.com/CollectionFS/Meteor-CollectionFS/tree/master/packages/s3
Note: Make sure not to place you secret key is not in you lib folder as this is accessible from the client.

node.js, css validation, w3c banned me?

I apologize in advance for what I write through a translator, I am very bad at English.
I was faced with the following problem: I need to perform validation css files. To this end, I decided to use the NPM package w3c-css, first it worked, but then start giving "connected etimedout", in the course of research, I noticed that through the browser and the validator stops working.
Sniffer log at start of my script: link (<10 rep :( )
My code:
gulp.task('css', function() {
gulp.src('dev/sass/*.scss')
.pipe(through2.obj(function(file, enc, cb){
w3c_css.validate({text: file.contents.toString('utf8')}, function(err, data) {
if(err) {
// an error happened
console.error(err);
} else {
// validation errors
console.log('validation errors', data.errors);
// validation warnings
console.log('validation warnings', data.warnings);
}
});
cb(null, file);
}))
.pipe(gulp.dest('build/'));
});
What is the reason? It must be some mistake, or I block due to too frequent requests and it does not change? Maybe there is some other way to check the css files?
Thx!
From the "About" page of the CSS validation service of the W3C:
Can I build an application upon this validator? Is there an API?
Yes, and yes. The CSS Validator has a (RESTful) SOAP interface which should make it reasonably easy to build applications (Web or otherwise) upon it. Good manners and respectful usage of shared resources are of course customary: make sure your applications sleep() between calls to the validator, or install and run your own instance of the validator.
So yes, it seems you have been banned.
I don't know how to make a gulp task to be called from time to time. You may mount a local version of the CSS Validator webservice and editing the w3c-css package to point to your own server.
Make sure that your script will sleep for at least 1 second between requests.
From the manual:
Note: If you wish to call the validator programmatically for a batch
of documents, please make sure that your script will sleep for at
least 1 second between requests. The CSS Validation service is a free,
public service for all, your respect is appreciated. thanks.
To validate multiple links, use async + setTimeout or any related way to pause between the requests:
'use strict';
var async = require('async');
var validator = require('w3c-css');
var hrefs = [
'http://google.com',
'https://developer.mozilla.org',
'http://www.microsoft.com/'];
async.eachSeries(hrefs, function(href, next) {
validator.validate(href, function(err, data) {
// { process err, data.errors & data.warnings }
// sleep for 1.5 seconds between the requests
setTimeout(function() { next(err); }, 1500);
});
}, function(err) {
if(err) {
console.log('Failed to process an url', err);
} else {
console.log('All urls have been processed successfully');
}
});
EDIT:
To mitigate this issue:
Added some comments and an example.
Placed setTimeout right into the gulp-w3c-css plugin.

Running Meteor methods that insert mongo documents in their own Fiber

I'm attempting to run ntwitter streaming API to track tweets about a certain hashtag, populating the Mongo collection Tweets with each tweet.
I've hooked it up server side like so:
t = new nTwitter({
consumer_key: credentials.consumer_key,
consumer_secret: credentials.consumer_secret,
access_token_key: credentials.access_token_key,
access_token_secret: credentials.access_token_secret
});
Meteor.methods({
trackTweets: function () {
this.unblock; // this doesn't seem to work
console.log('... ... trackTweets');
var _this = this;
t.stream(
'statuses/filter',
{ track: ['#love'] },
function(stream) {
stream.on('data', function(tweet) {
// app/packages/mongo-livedata/collection.js:247
// throw e;
// ^
// O yes I love her like money
// Error: Meteor code must always run within a Fiber
console.log(tweet.text);
Tweets.insert(tweet.text); // this call blocks
});
stream.on('error', function(error, code) {
console.log("My error: " + error + ": " + code);
});
}
);
}
});
The line: Tweets.insert(tweet.text) throws the must run inside its own Fiber error – and I've tried putting the this.unblock statement in several different places.
What should I do here?
you dont call the function unblock, you need to replace your
this.unblock;
with this:
this.unblock();
if that doesn't work i would think it has something to do with the way ntwitter is getting the data, you could try to add this
if (Meteor.isClient) return false;
so that the method doesn't run on the client, but only on the server
I believe the code you are running server-side needs to be contained within a Fiber.
Some similar examples can be found in these answers:
Meteor code must always run within a Fiber” when calling Collection.insert on server
Stream stdout to Meteor website

Resources