On this page:
- Description
- Supported Node.js versions
- Function Point, Quality and Sizing support
- CAST AIP compatibility
- Supported DBMS servers
- Prerequisites
- Dependencies with other extensions
- Download and installation instructions
- Packaging, delivering and analyzing your source code
- What results can you expect?
- Known Limitations
Target audience:
Users of the extension providing Node.js + Express support for Web applications.
Summary: This document provides basic information about the extension providing Node.js + Express support for Web applications.
What's New
- Added Node.js Express Controller to have better Model-View-Controller transactions
- Support Express Router system to better manage URL calls.
- Issue fixed when NoSQL calls are made from Javascript methods
Description
This extension provides support for Node.js. Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.
In what situation should you install this extension?
Regarding Front-End to Back-End connections, we do support the following cross-technology stacks:
iOS Front-End connected to Node.js/PostgreSQL Back-end | iOS Front-End connected to Node.js/MSSQL Back-end | AngularJS Front-End connected to Node.js/MongoDB Back-end |
If your Web application contains Node.js source code and you want to view these object types and their links with other objects, then you should install this extension:
- creates a Node.js application object when an instance has been found
- creates Node.js operations which represent entry-points of web services
These declarations will create a Node.js Get Operation:
app.get('/login', function (req, res) { "use strict"; console.log('login ' + req.url); console.log('login ' + req.query.pseudo); var currentSession = getSessionId(req, res); datab.userExists(currentSession, req.query.pseudo, res, cbLogin); });
and this one will create a NodeJS Service Operation
var admin = express(); app.use('/admin', admin);
Node.js operations are called from client applications, using jQuery Ajax for example. Supported client frameworks are:
Supported Node.js versions
Version | Support | Comment |
---|---|---|
v0.x | No longer supported | |
v4.x | LTS | |
v5.x | Based on Javascript ES6 | |
v6.x | Based on Javascript ES6 | |
v7.x | Based on Javascript ES6 | |
v8.x |
- Function Points (transactions): a green tick indicates that OMG Function Point counting and Transaction Risk Index are supported
- Quality and Sizing: a green tick indicates that CAST can measure size and that a minimum set of Quality Rules exist
Function Points (transactions) | Quality and Sizing | Security |
---|---|---|
Comparison with existing support for JavaScript in CAST AIP
CAST AIP has provided support for analyzing JavaScript via its JEE and .NET analyzers (provided out of box in CAST AIP) for some time now. The HTML5/JavaScript extension (on which the Node.js extension depends) also provides support for JavaScript but with a focus on web applications. CAST highly recommends that you use this extension if your Application contains JavaScript and more specifically if you want to analyze a web application, however you should take note of the following:
- You should ensure that you configure the extension to NOT analyze the back end web client part of a .NET or JEE application.
- You should ensure that you configure the extension to ONLY analyze the front end web application built with the HTML5/JavaScript that communicates with the back end web client part of a .NET or JEE application.
- If the back end web client part of a .NET or JEE application is analyzed with the Node.js extension and with the native .NET/JEE analyzers, then your results will reflect this - there will be duplicate objects and links (i.e. from the analyzer and from the extension) therefore impacting results and creating erroneous Function Point data.
Note that in CAST AIP ≥ 8.3.x support for analyzing JavaScript has been withdrawn from the JEE and .NET analyzers.
CAST AIP release | Supported |
---|---|
8.3.x | |
8.2.x | |
8.1.x | |
8.0.x | |
7.3.4 and all higher 7.3.x releases |
Supported DBMS servers
This extension is compatible with the following DBMS servers:
CAST AIP release | CSS2 | Oracle | Microsoft |
---|---|---|---|
All supported releases |
Prerequisites
An installation of any compatible release of CAST AIP (see table above) |
Dependencies with other extensions
Some CAST extensions require the presence of other CAST extensions in order to function correctly. The Node.js extension requires that the following other CAST extensions are also installed:
- HTML5/JavaScript
- Web services linker service (internal technical extension)
Download and installation instructions
Please see:
- http://doc.castsoftware.com/display/EXTEND/Download+an+extension
- http://doc.castsoftware.com/display/EXTEND/Install+an+extension
Packaging, delivering and analyzing your source code
Once the extension is downloaded and installed, you can nowpackage your source code and run an analysis. The process of packaging, delivering and analyzing your source code is described below:
What results can you expect?
Once the analysis/snapshot generation has completed, you can view the results in the normal manner (for example via CAST Enlighten):
Node.js application with MongoDB data storage exposing web services
Objects
The following specific objects are displayed in CAST Enlighten:
Icon | Description |
---|---|
Node.js Application | |
Node.js Port | |
Node.js Delete Operation Service | |
Node.js Get Operation Service | |
Node.js Post Operation Service | |
Node.js Put Operation Service | |
Node.js Service | |
Node.js Express Use | |
Node.js Express Controller | |
Node.js Get Http Request Service | |
Node.js Post Http Request Service | |
Node.js Put Http Request Service | |
Node.js Delete Http Request Service | |
Node.js Unknown Database | |
Node.js MongoDB Connection | |
Node.js MongoDB Collection | |
Node.js Mongoose Connection | |
Node.js Marklogic Database | |
Node.js Marklogic Collection | |
Node.js CouchDB database |
External link behaviour
Behaviour is different depending on the version of CAST AIP you are using the extension with:
- From 7.3.6, SQL queries are sent to the external links exactly like standard CAST AIP analyzers.
- From 7.3.4 and before 7.3.6, a degraded mode takes place: The Node.js extension analyzes the FROM clause to retrieve table names, then sends the table names only to external links.
- For all versions, if no links are found via external links, unresolved objects are created (with type CAST_NodeJS_Unknown_Database_Table).
Connector per RDBMS Vendor
Oracle "oracledb" connector
var oracledb = require('oracledb'); connection = oracledb.getConnection( { user : "hr", password : "welcome", connectString : "localhost/XE" } ); connection.execute( "SELECT department_id, department_name FROM departments WHERE department_id < 70", function(err, result) { if (err) { console.error(err); return; } console.log(result.rows); } );
MS SQL "node-sqlserver" and "mssql" connectors
var sql = require('node-sqlserver'); // var connStr = "Driver={SQL Server Native Client 11.0};Server=myySqlDb,1433;Database=DB;UID=Henry;PWD=cat;"; var query = "SELECT * FROM GAData WHERE TestID = 17"; sql.open(connStr, function(err,conn){ if(err){ return console.error("Could not connect to sql: ", err); } conn.queryRaw("SELECT TOP 10 FirstName, LastName FROM authors", function (err, results) { if (err) { console.log("Error running query!"); return; } for (var i = 0; i < results.rows.length; i++) { console.log("FirstName: " + results.rows[i][0] + " LastName: " + results.rows[i][1]); } }); }); var match = "%crombie%"; sql.query(conn_str, "SELECT FirstName, LastName FROM titles WHERE LastName LIKE ?", [match], function (err, results) { for (var i = 0; i < results.length; i++) { console.log("FirstName: " + results[i].FirstName + " LastName: " + results[i].LastName); } });
var sql = require('mssql'); var config = { user: '...', password: '...', server: 'localhost', // You can use 'localhost\\instance' to connect to named instance database: '...', options: { encrypt: true // Use this if you're on Windows Azure } } var connection = new sql.Connection(config, function(err) { // ... error checks // Query var request = new sql.Request(connection); // or: var request = connection.request(); request.query('select * from authors', function(err, recordset) { // ... error checks console.dir(recordset); }); // Stored Procedure var request = new sql.Request(connection); request.input('input_parameter', sql.Int, 10); request.output('output_parameter', sql.VarChar(50)); request.execute('procedure_name', function(err, recordsets, returnValue) { // ... error checks console.dir(recordsets); }); });
PostgreSQL "pg" connector
var pg = require("pg"); var conString = "pg://operator:CastAIP@localhost:2280/postgres"; var client = new pg.Client(conString); client.connect(); var querySchemas = client.query("select nspname from pg_catalog.pg_namespace"); querySchemas.on("row", function (row, result) { "use strict"; result.addRow(row); }); querySchemas.on("end", function (result) { "use strict"; console.log(result.rows); client.end(); });
MySQL "my_connection" connector
var connection = require("my_connection"); connection.query('my_url', function result_getCatLogDetails(getCatLogDetails_err, getCatLogDetails_rows, getCatLogDetails_fields) { if (getCatLogDetails_err) { logContent += '|ERROR'+";"; logContent += getCatLogDetails_err.message+";"; utils.logAppDetails(logContent); deferred.reject(new Error(getCatLogDetails_err)); } else { deferred.resolve(getCatLogDetails_rows); } });
Connector per NoSQL Vendor
Even if we don't have NoSQL server side representation, we will create a client side representation based on the API access. Node.js analyzer will create links from Javascript functions to NoSQL "Database" or "Tables" equivalents
MongoDB "mongoose" connector
This declaration will create a MongoDB connection and model object
var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/analyzerlauncher', function(err) { if (err) { throw err; } }); // create a Mongoose model userModel = mongoose.model('users', userSchema);
MarkLogic "marklogic" connector
This declaration will create a MarkLogic database and a collection
var db = marklogic.createDatabaseClient(conn); // create Collection countries var q = marklogic.queryBuilder; db.documents.query( q.where( q.collection('countries'), q.value('region', 'Africa'), q.or( q.word('background', 'France'), q.word('Legal system', 'French') ) ) ).result(function(documents) { documents.forEach(function(document) { console.log(JSON.stringify(document)); }); });
This declaration will create a marklogic collection "fake data" and a useDeleteLink to the collection "fake data".
db.documents.removeAll({collection: 'fake data'}) .result() .then(response => console.log('Removed collection ' + response.collection)) .catch(error => console.log(error));
This declaration will create a marklogic collection "fake data" and a useInsertLink to the collection "fake data".
db.documents.write( data.map((item) => { return { uri: `/${item.guid}.json`, contentType: 'application/json', collections: ['fake data'], content: item } }) ) .result() .then(response => console.dir(JSON.stringify(response))) .catch(error => console.error(error));
CouchDB "node-couchdb" connector
This declaration will create a CouchDB database named "myDatabase".
const NodeCouchDb = require('node-couchdb'); // node-couchdb instance with default options const couch = new NodeCouchDb(); dbName = 'myDatabase'; couch.createDatabase(dbName).then(() => { }, err => { // request error occured });
This declaration will create a useSelectLink from code to the database "myDatabase".
couch.get("myDatabase", "some_document_id").then(({data, headers, status}) => { // data is json response // headers is an object with all response headers // status is statusCode number }, err => { // either request error occured // ...or err.code=EDOCMISSING if document is missing // ...or err.code=EUNKNOWN if statusCode is unexpected });
This declaration will create a useInsertLink from code to the database "myDatabase".
couch.insert("myDatabase", { _id: "document_id", field: ["sample", "data", true] }).then(({data, headers, status}) => { // data is json response // headers is an object with all response headers // status is statusCode number }, err => { // either request error occured // ...or err.code=EDOCCONFLICT if document with the same id already exists });
This declaration will create a useUpdateLink from code to the database "myDatabase".
couch.update("myDatabase", { _id: "document_id", _rev: "1-xxx" field: "new sample data", field2: 1 }).then(({data, headers, status}) => { // data is json response // headers is an object with all response headers // status is statusCode number }, err => { // either request error occured // ...or err.code=EFIELDMISSING if either _id or _rev fields are missing });
This declaration will create a useDeleteLink from code to the database "myDatabase".
couch.del("myDatabase", "some_document_id", "document_revision").then(({data, headers, status}) => { // data is json response // headers is an object with all response headers // status is statusCode number }, err => { // either request error occured // ...or err.code=EDOCMISSING if document does not exist // ...or err.code=EUNKNOWN if response status code is unexpected });
CouchDB "couchdb" connector
This declaration will create a CouchDB database named "myDatabase".
var myCouchDB = require('couch-db').CouchDB, server = new myCouchDB('http://localhost:5984'); server.auth(username, password); server.bind('myDatabase'); var db = server.myDatabase;
These declarations will create a useInsertLink from code to the database "myDatabase".
// new document var doc = db.testdb.doc({}); doc.attach([{ name: 'place.css', content_type: 'text/css', data: 'body { font-size: 12px; }' }, { name: 'script.js', content_type: 'script/javascript', data: 'window.onload(function() {})' }]).create(function(err) { });
db.destroy(function(err) { // create a new database db.create(function(err) { // insert a document with id 'jack johns' db.insert({ _id: 'jack johns', name: 'jack' }, function(err, body) { if (err) { console.log('insertion failed ', err.message); return; } console.log(body); // body will like following: // { ok: true, // id: 'jack johns', // rev: '1-610953b93b8bf1bae12427e2de181307' } }); }); });
This declaration will create a useUpdateLink from code to the database "myDatabase".
var doc = db.testdb.doc({}); // open to get revision or assign revision to the document doc.open(function(err) { doc.attach('plain.css', 'body { font-size:12pt; }', 'text/css'); // save the doc doc.save(function(err, rs) { var plain = doc.attachment('plain.txt'); // retrieve attachment plain.get(function(err, body) { assert.equal(body, 'body { font-size:12pt; }'); // update plain.update('body { font-size:14pt; }', 'text/css', function(err) { plain.get(function(err, body) { assert.equal(body, 'body { font-size:14pt; }'); }); }); }); });
Rules
List of rules is available here:
https://technologies.castsoftware.com/?rlH=extensions/com.castsoftware.nodejs/1.6.0-funcrel.json
Security Coverage
Node.js analyzer is able to analyze OWASP NodeGoat https://github.com/OWASP/NodeGoat and provide the following security checks.
Known Limitations
In this section we list the most significant functional limitations that may affect the analysis of applications using Node.js:
- With regard to external links degraded mode, only statements with a FROM clause are correctly handled.