On this page:

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

  • New security rules:
    • 1020736 : Avoid bypassing self-signed ssl certificate (Node.js)
    • 1020738 : Avoid disabling SSL verification in node-curl
    • 1020740 : Avoid creating cookie with overly broad path (Node.js)
    • 1020742 : Avoid having cookie with an overly broad domain (Node.js)
  • Minor issues fixed

Description

This extension provides support for Node.jsNode.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-endiOS Front-End connected to Node.js/MSSQL Back-endAngularJS 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:

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);

Supported Node.js versions

VersionSupportComment
v0.x(error)No longer supported
v4.x(tick)LTS
v5.x(tick)Based on Javascript ES6
v6.x(tick)Based on Javascript ES6

v7.x

(tick)Based on Javascript ES6
v8.x(tick) 
v9.x(tick)
v10.x(tick)

Function Point, Quality and Sizing support

This extension provides the following support:

Function Points
(transactions)
Quality and SizingSecurity
(tick)(tick)(tick)

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:

Note that in CAST AIP 8.3.x support for analyzing JavaScript has been withdrawn from the JEE and .NET analyzers.

CAST AIP compatibility

This extension is compatible with:

CAST AIP release
Supported
8.3.x(tick)
8.2.x(tick)
8.1.x(tick)
8.0.x(tick)
7.3.4 and all higher 7.3.x releases(tick)

Supported DBMS servers

This extension is compatible with the following DBMS servers:

CAST AIP releaseCSS2OracleMicrosoft
All supported releases(tick)(tick)(error)

Prerequisites

(tick)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:

Note that when using the CAST Extension Downloader to download the extension and the Manage Extensions interface in CAST Server Manager to install the extension, any dependent extensions are automatically downloaded and installed for you. You do not need to do anything.

Download and installation instructions

Please see:

The latest release status of this extension can be seen when downloading it from the CAST Extend server.

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:

IconDescription

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:



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/rules?rlH=AIP/extensions/com.castsoftware.nodejs/versions/1.8.0-funcrel/quality-rules

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: