Loopback getting error - Major change in User validatePassword function in the same release itself (3.0.0) - node.js

I am using loopback 3.0.0, and I have set up a new server recently, about one week ago. For that, I have ran the command npm install by putting the package.son file.
But in that installed files, the node_modules/loopback/common/user.js module has changed with major changes.
Egs:
Old file:
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
User.validatePassword = function(plain) {
var err;
if (plain && typeof plain === 'string' && plain.length <= MAX_PASSWORD_LENGTH) {
return true;
}
if (plain.length > MAX_PASSWORD_LENGTH) {
err = new Error(g.f('Password too long: %s', plain));
err.code = 'PASSWORD_TOO_LONG';
} else {
err = new Error(g.f('Invalid password: %s', plain));
err.code = 'INVALID_PASSWORD';
}
err.statusCode = 422;
throw err;
};
New File:
// Copyright IBM Corp. 2014,2018. All Rights Reserved.
User.validatePassword = function(plain) {
var err;
if (!plain || typeof plain !== 'string') {
err = new Error(g.f('Invalid password.'));
err.code = 'INVALID_PASSWORD';
err.statusCode = 422;
throw err;
}
// Bcrypt only supports up to 72 bytes; the rest is silently dropped.
var len = Buffer.byteLength(plain, 'utf8');
if (len > MAX_PASSWORD_LENGTH) {
err = new Error(g.f('The password entered was too long. Max length is %d (entered %d)',
MAX_PASSWORD_LENGTH, len));
err.code = 'PASSWORD_TOO_LONG';
err.statusCode = 422;
throw err;
}
};
I have developed my code with the same version but with old code which they have provided in the same version(3.0.0.). Here you can see, in the new code there is no return statement, so the code is infinitely waiting for the return and being time out. In both places the package.json file contains the same version: "loopback": "^3.0.0"
I hope it's not recommended to copy the node_modules from our developement server to production server.
So how can we handle these type of issues?

When specifying a version number in the package.json there are a few different ways https://docs.npmjs.com/files/package.json#dependencies:
The way you have is the default, ^ which means
compatible with version
So ^3.0.0 will only install 3.0.0 if it is the latest minor and fix versions otherwise it will take whatever the latest version of loopback is on that day. Today that is 3.19.3.
The issue was introduced in version v3.10.1(thanks #vasan) so locally maybe you had version 3.10.0 then on the server you had 3.10.1
There is a good explanation about the version numbers in this question What's the difference between tilde(~) and caret(^) in package.json?
I would suggest using an exact version, i.e. 3.19.3 then using a service like rennovate, https://github.com/renovate-bot, to update your project to keep up to date with security patches
There is also another guard against this, package-lock.json https://docs.npmjs.com/files/package-lock.json introduced in version 5 of npm. If you check this file in it will make sure that wherever you run npm install the exact version of the npm module is installed wherever you run it.

Related

How to fix: Error: If .NET source is provided as JavaScript function, function body must be a /* ... */ comment

I am trying to run the basic hello world example from the documentation from here (it's the first example on the page):
https://github.com/agracio/edge-js
I have a typescript file that I am running (see the code below). I am on node version 9.9.0 on a Windows 10 64 bit. I have made only the following installations:
npm install edge
npm install edge.js
npm install #types/node --save-dev
I have made the installations to the same directory as the typescript file.
I am able to run ts-node app.ts in the command line and have it console.log("hi") successfully in that directory.
However, when I change my code to the example below, it is giving me the error throw new Error('If .NET source is provided as JavaScript function, function body must be a /* ... */ comment.');
I had originally tried doing this using edge.js but I kept getting the error that I needed to precompile. For the life of me I couldn't get it to find my python executable when executing build.bat release 10.5.3. (Despite including an environment variable PYTHON with a value of c:\Python\Python37\python.exe) I wanted to try using edge-js because it was already precompiled. I downgraded node to 9.9 (I uninstalled node 10.15.3 and then installed the 9.9.0 msi from the website) because I thought edge-js only supported version 9. Well here I am trying to run edge-js with version 9 and I am still getting an error, although it is a different error.
Here is the code I am trying to run:
var edge = require('edge-js');
var helloWorld = edge.func(function () {/*
async (input) => {
return ".NET Welcomes " + input.ToString();
}
*/});
helloWorld('JavaScript', function (error, result) {
if (error) throw error;
console.log(result);
});
I can't believe this worked. It was a syntax error. A problem with the amount of whitespace between the comment and the end of the function. Here is the correct syntax:
var edge = require('edge-js');
var helloWorld = edge.func(function () {
/*async (input) => {
return ".NET Welcomes " + input.ToString();
}*/
});
helloWorld('JavaScript', function (error, result) {
if (error) throw error;
console.log(result);
});

Create resilient require mechanism in Node.js

I have an application and I want it to be resilient against missing dependencies. Some sort of fallback mechanism, in case a dependency is missing or corrupted.
So I have this, which monkey-patches require() and in theory will re-install a dependency if it cannot be loaded the first time:
const cp = require('child_process');
const Mod = require('module');
const req = Mod.prototype.require;
Mod.prototype.require = function (d) {
try {
return req.apply(this, arguments);
}
catch (e) {
if (/^[A-Za-z]/.test(String(d))) {
console.error(`could not require dependency "${d}".`);
try {
var actualDep = String(d).split('/')[0];
console.error(`but we are resilient and we are attempting to install "${actualDep}" now.`);
cp.execSync(`cd ${_suman.projectRoot} && npm install ${actualDep}`);
return req.apply(this, arguments);
}
catch (err) {
console.error('\n', err.stack || err, '\n');
throw e;
}
}
else {
throw e;
}
}
};
but the above patch is not working as expected. The problem is that the initial try statement:
return req.apply(this, arguments);
it mostly works, but it's doing some strange things..for example, it starts installing
bufferutil
utf-8-validate
this doesn't make sense to me, because require() works without any errors normally, but when using the patch, require() suddenly fails.
Anyone know what might be going on? Super weird.
(Note what it's doing is only attempting to re-install dependencies that start with a character [a-zA-z], it won't attempt to install a dependency that starts with './' etc.)

What is the best way to run npm packages on demand as mircoservices without installing it locally?

Let's say I have these npm packages published to npm:
service1#v1.0
service1#v2.0
service2#v1.0
each package has a single function:
function run(extraStr) {
return 'package_name_and_version' + extraStr; // i.e. service1 v1.0 extraStr
}
And I want to write nodejs code that use the packages without installing it locally
var server = require("my-server-sdk");
// get(package_name, version, function_in_package, arguments, callback)
server.get('service1', '2.0', 'run', ['app1'], (err, result) => {
console.log(result); // this should print service1 v2.0 app1
});
where my-server-sdk is an sdk that interface with my server's api where it install the required packages and cache it for later use.
What is the best way to do that? what the security concerns and how to prevent any?
this is a simple diagram for what I want
NOTE: service1#v1.0
service1#v2.0
service2#v1.0
are just examples to any packages in npm i.e. lodash
Caching example:
Let's say we have TTL equal 60 minutes.
client1 requested a function from lodash and another function from underscore at 01:00.
Now in the server lodash and underscore are installed with timestamp 01:00.
client2 requested a function from underscore at 01:30 which get used instantly because underscore is installed before but it timestamp got updated to 1:30.
At 02:01 lodash get deleted because it didn't get used on the past TTL currenttime - lodash_timestamp > TTL but underscore stays because currenttime - underscore_timestamp < TTL
So when client3 request lodash at 02:30 it get intsalled again with 02:30 as a timestamp.
There is the npmi package which gives an API to npm install.
The logic I would use is:
Get the specific package and version from npm (install if is not already installed)
Require the package inside nodejs
Run the specified method with the specified parameters
Return the results to the client
var npmi = require('npmi');
var path = require('path');
function runModule(moduleName, moduleVersion, moduleMethod, moduleMethodParams) {
return new Promise((resolve, reject) => {
var options = {
name: moduleName, // your module name
version: moduleVersion, // expected version [default: 'latest']
forceInstall: false, // force install if set to true (even if already installed, it will do a reinstall) [default: false]
npmLoad: { // npm.load(options, callback): this is the "options" given to npm.load()
loglevel: 'silent' // [default: {loglevel: 'silent'}]
}
};
options.path = './' + options.name + "#" + options.version,
npmi(options, function(err, result) {
if (err) {
if (err.code === npmi.LOAD_ERR) console.log('npm load error');
else if (err.code === npmi.INSTALL_ERR) console.log('npm install error');
console.log(err.message);
return reject(err)
}
// installed
console.log(options.name + '#' + options.version + ' installed successfully in ' + path.resolve(options.path));
var my_module = require(path.resolve(options.path, "node_modules", options.name))
console.log("Running :", options.name + '#' + options.version)
console.log("Method :", moduleMethod);
console.log("With params :", ...moduleMethodParams)
resolve(my_module[moduleMethod](...moduleMethodParams))
});
})
}
runModule('lodash', '4.10.0', 'fill', [Array(3), 2])
.then(result => console.log("Result :", result))
runModule('lodash', '3.10.0', 'fill', [Array(3), 2])
.then(result => console.log("Result :", result))
You could see now that there are 2 created folders (lodash#3.10.0 , lodash#4.10.0) indicating the package name and version.
I have made the assumptions that npm is in path and the server has the permissions to install packages in current directory, also that the "MODULE_NAME#MODULE_VERSION" is a valid folder name under the current OS.

Get list of all NPM dependencies minimum node.js engine version

We have an NPM project X. I want to get a distinct list of all the dependencies in the project and the minimum Node.js (engine) version that they need.
How can I do this?
The motivation is of course to discovery what the minimum Nodejs version we need to run in development and production.
npm ls | grep "engines"
something like that, except the above won't work, hopefully there is something more robust
I was able to accomplish this like so:
let npm = require('npm');
npm.load({}, function(err, npm) {
if(err) throw err;
npm.config.set('global', false); // => we don't want to consider global deps
npm.commands.list([], true, function(err, pkgInfo) {
let enginesList = Object.keys(pkgInfo.dependencies).map(function(k){
return {
dep: k,
engines: pkgInfo.dependencies[k].engines || {}
}
});
enginesList.forEach(function(val){
console.log(val.dep + ' => ', val.engines);
});
});
});

How do I require a minimum version of node.js in my script?

I just found out that a script I wrote only works on node 0.10 because it uses readable events.
How do I require a minimum version of node.js in my script so that users know that they need to upgrade?
In package.json:
{ "engines" : { "node" : ">=0.10.3" } }
From the docs.
Edit, a programmatic way:
var pkg = require('./pacakge'),
semver = require('semver');
if(!semver.satisfies(process.version, pkg.engines.node)) {
// Not sure if throw or process.exit is best.
throw new Error('Requires a node version matching ' + pkg.engines.node);
}
Add this to top the top of your script.
var versionComps = process.versions['node'].split('.');
if (parseInt(versionComps[0]) === 0 && parseInt(versionComps[1]) < 10) {
console.log('Script requires node.js version >= 0.10');
process.exit(1);
};

Resources