diff --git a/.cfignore b/.cfignore
index b512c09d..2099835f 100644
--- a/.cfignore
+++ b/.cfignore
@@ -1 +1,3 @@
-node_modules
\ No newline at end of file
+node_modules
+run.sh
+VCAP_SERVICES.json
diff --git a/.gitignore b/.gitignore
index 28f1ba75..08f7c23e 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
node_modules
-.DS_Store
\ No newline at end of file
+.DS_Store
+run.sh
+VCAP_SERVICES.json
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..bec59a05
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,25 @@
+language: node_js
+sudo: true
+node_js:
+- stable
+script:
+- npm test
+env:
+ global:
+ - CF_APP=tone-analyzer-demo
+ - CF_API=https://api.ng.bluemix.net
+ - CF_ORGANIZATION=WatsonPlatformServices
+ - CF_SPACE=demos
+ - secure: "1ArUhDyjOH/g7MdGNRCDrDintTMPk1bihDB7ZjTv9p+nxQS090r8VtnkeQgo+yzaK8HHLiG14LFiaWK6eIVf6HnlgyeX3Hh7KsF/rnb0w7TPbPvYpolmDHyLB7FdWtLVVTZPVUpxkiJID6PEh/ENSnC1GWDGpIn9LlyElmVt5sMZAXqs77XJNT0ogxHz1OjZB7UqWwsEPrNmdPEPGkRloxcDdM3m19VCmwL/WoI1a1T9r48L7p8IeM5bQ+olIWnNzoboOw2O+gEUzzHeE8ufXp9eWSju6fO8JCPkNLsGMpVwR8aIoFvOcZPE5W7UfzAeQkYwuayDndqg+lFZd32+tFMr8e6OT590B1fI8Ut9F4cRkGG5ivV16/SJJKrWRp0vRiGZ/N7yCbW+YE2OcsxquXDt5VqRPqW+nAjMCw7ADxq5C3zBMxQK9ST1p0pTVJLLrIq0QUjfYhTEjYufeWaxLXgVdyXE6QLm1Z2REjXLBzUs8Wc1AkJbbhfJ03RJvj39A3dlNgy8F5UbC3AVAQ817xaRPcho4LakfDulTvUooIL0j5ROh81Q/d/DDlnrs4ymlsGqNmJ7ZfMxvtXDesFyy+ZlqmxxRTA8NOpzBMSHP4GZ9oq+HMudPyVVaZexSewouBXj6bOG0uNG8iWa8YCKDnlxiNAmCEEodcLlb+XEhVQ="
+ - secure: "bFhB3yakclo4ebxm63JN2BBAptN/UI/OrIBMqeK0a6AufHekbwJWxPjtt3yqruKnop1aLfkTi8MF8C8eQOp8NZ8oVRBzjGmv3h+cTFD9cgcig7Ut0660/TwABIJv8rRMTaOy3CxsRKAVFjMnFFx6YWbKu1+T+5EUfjIiXALzEVOfAr67Y1aR6ACApS9GlUJ2PDErdQVcm6JJ81h/TLLOmmnUoTGv/5O+OUUxzwwLeW+f5jMVgoIUfN9+z1SWzPkBdZhHT91KeihPl1GgBeonBWpCQMGbCuEbB97K0BrbExvZXjqcs4avJr/u2SIOP9NFcw69O59ey5LTBNhW3PcMDDtGFiQ5fjCjP9ldWSnBXYcVd3V9FatX/KnHOE8GtXzCVZt3W3cMnL0s/9wfhfkBuuZscKE8hbKbcMXe9Xtc1Qik8hefCmCWek3utFtW2mLklVo9Y3Ec+p7h7cq7k1LI6zCxHolAGXBu2fXoF6ErJKW1Y8WB4b6EJnpb+XUUgBmw8yZDVDdTKONYDg+EgEqUYB4kzSbvfgH6i8A9nb6rSY3qJUJoj+q5h/EBAxtFz4YEb6QVrT3gkE8acnYRgAKTukX5t5uwO1B7svBaWjZvxfaTBx0GSQmTMNXThgM62ExKEEfkZr5i9Dgu83pGXBEyGS9T+p6gHziE8bZfvU9SWK8="
+before_deploy: npm install -g cf-blue-green
+deploy:
+ provider: script
+ script:
+ - cf-blue-green-travis
+ on:
+ branch: master
+ repo: watson-developer-cloud/tone-analyzer-nodejs
+ skip_cleanup: true
+notifications:
+ email: false
diff --git a/README.md b/README.md
index ae0f69e5..6ce022b0 100755
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
# Tone Analyzer Node.js Starter Application
+[![Build Status](https://travis-ci.org/watson-developer-cloud/tone-analyzer-nodejs.svg?branch=master)](http://travis-ci.org/watson-developer-cloud/tone-analyzer-nodejs)
+[![codecov.io](https://codecov.io/github/watson-developer-cloud/tone-analyzer-nodejs/coverage.svg?branch=master)](https://codecov.io/github/watson-developer-cloud/tone-analyzer-nodejs?branch=master)
The IBM Watson [Tone Analyzer][service_url] service is a cognitive linguistic analysis service that detects three types of tones from written text: emotions, social tendencies, and writing style. Emotions identified include things like anger, fear, joy, sadness, and disgust. Identified social tendencies include things from the Big Five personality traits used by some psychologists. These include openness, conscientiousness, extraversion, agreeableness, and neuroticism. Identified writing styles include confident, analytical, and tentative.
@@ -109,8 +111,25 @@ To troubleshoot your Bluemix application, use the logs. To see the logs, run:
See [CONTRIBUTING](CONTRIBUTING.md).
## Open Source @ IBM
+
Find more open source projects on the [IBM Github Page](http://ibm.github.io/)
+### Privacy Notice
+
+This node sample web application includes code to track deployments to Bluemix and other Cloud Foundry platforms. The following information is sent to a [Deployment Tracker][deploy_track_url] service on each deployment:
+
+* Application Name (`application_name`)
+* Space ID (`space_id`)
+* Application Version (`application_version`)
+* Application URIs (`application_uris`)
+
+This data is collected from the `VCAP_APPLICATION` environment variable in IBM Bluemix and other Cloud Foundry platforms. This data is used by IBM to track metrics around deployments of sample applications to IBM Bluemix. Only deployments of sample applications that include code to ping the Deployment Tracker service will be tracked.
+
+### Disabling Deployment Tracking
+
+Deployment tracking can be disabled by removing `require('cf-deployment-tracker-client').track();` from the beginning of the `server.js` file at the root of this repo.
+
+[deploy_track_url]: https://github.com/cloudant-labs/deployment-tracker
[service_url]: http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/tone-analyzer.html
[cloud_foundry]: https://github.com/cloudfoundry/cli
[getting_started]: http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/doc/getting_started/
diff --git a/app.js b/app.js
index 942913f7..7dc24986 100755
--- a/app.js
+++ b/app.js
@@ -33,7 +33,10 @@ var toneAnalyzer = watson.tone_analyzer({
});
app.get('/', function(req, res) {
- res.render('index');
+ res.render('index', {
+ ct: req._csrfToken,
+ ga: process.env.GOOGLE_ANALYTICS
+ });
});
app.post('/api/tone', function(req, res, next) {
@@ -48,6 +51,4 @@ app.post('/api/tone', function(req, res, next) {
// error-handler application settings
require('./config/error-handler')(app);
-var port = process.env.VCAP_APP_PORT || 3000;
-app.listen(port);
-console.log('listening at:', port);
+module.exports = app;
diff --git a/config/express.js b/config/express.js
index 9baa3478..da1545ff 100755
--- a/config/express.js
+++ b/config/express.js
@@ -32,8 +32,8 @@ module.exports = function (app) {
// Setup static public directory
app.use(express.static(__dirname + '/../public'));
- // Only loaded when SECURE_EXPRESS is `true`
- if (process.env.SECURE_EXPRESS)
+ // Only loaded when VCAP_APPLICATION is `true`
+ if (process.env.VCAP_APPLICATION)
require('./security')(app);
-};
\ No newline at end of file
+};
diff --git a/config/security.js b/config/security.js
index 680e55a6..860327a4 100644
--- a/config/security.js
+++ b/config/security.js
@@ -17,21 +17,19 @@
'use strict';
// security.js
-var secure = require('express-secure-only'),
- rateLimit = require('express-rate-limit'),
- helmet = require('helmet');
+var rateLimit = require('express-rate-limit'),
+ helmet = require('helmet'),
+ csrf = require('csurf'),
+ cookieParser = require('cookie-parser');
module.exports = function (app) {
app.enable('trust proxy');
- // 1. redirects http to https
- app.use(secure());
+ // 1. helmet with defaults
+ app.use(helmet({ cacheControl: false }));
- // 2. helmet with defaults
- app.use(helmet());
-
- // 3. rate limiting
- var limiter = rateLimit({
+ // 2. rate limiting
+ app.use('/api/', rateLimit({
windowMs: 30 * 1000, // seconds
delayMs: 0,
max: 6,
@@ -39,7 +37,17 @@ module.exports = function (app) {
error:'Too many requests, please try again in 30 seconds.',
code: 429
}),
- });
+ }));
- app.use('/api/', limiter);
+ // 3. setup cookies
+ var secret = Math.random().toString(36).substring(7);
+ app.use(cookieParser(secret));
+
+ // 4. csrf
+ var csrfProtection = csrf({ cookie: true });
+ app.get('/', csrfProtection, function(req, res, next) {
+ console.log(req.csrfToken());
+ req._csrfToken = req.csrfToken();
+ next();
+ });
};
diff --git a/manifest.yml b/manifest.yml
index 618d02d8..b3e762f5 100644
--- a/manifest.yml
+++ b/manifest.yml
@@ -7,9 +7,7 @@ applications:
- services:
- tone-analyzer-service
name: tone-analyzer-demo
- command: node app.js
+ command: npm start
path: .
- memory: 512MB
- env:
- NODE_ENV: production
- SECURE_EXPRESS: 1
\ No newline at end of file
+ instances: 2
+ memory: 512MB
\ No newline at end of file
diff --git a/package.json b/package.json
index 3286db4d..fd84550a 100644
--- a/package.json
+++ b/package.json
@@ -26,10 +26,16 @@
"url": "https://github.com/watson-developer-cloud/tone-analyzer-nodejs/issues"
},
"scripts": {
- "start": "node app.js"
+ "start": "node server.js",
+ "test": "npm run jshint && (npm run codecov || true)",
+ "jshint": "jshint --exclude ./node_modules/",
+ "codecov": "istanbul cover ./node_modules/mocha/bin/_mocha && codecov"
},
"dependencies": {
"body-parser": "^1.13.2",
+ "cf-deployment-tracker-client": "0.0.8",
+ "cookie-parser": "^1.4.1",
+ "csurf": "^1.8.3",
"ejs": "^2.3.4",
"errorhandler": "^1.4.1",
"express": "^4.12.2",
@@ -37,5 +43,13 @@
"express-secure-only": "^0.2.1",
"helmet": "^1.3.0",
"watson-developer-cloud": "^1.2.5"
+ },
+ "devDependencies": {
+ "codecov": "^1.0.1",
+ "istanbul": "^0.4.2",
+ "jshint": "^2.9.1",
+ "mocha": "^2.4.5",
+ "nock": "7.7.0",
+ "supertest": "^1.2.0"
}
}
diff --git a/public/js/demo.js b/public/js/demo.js
index 6202175a..4be0cc60 100755
--- a/public/js/demo.js
+++ b/public/js/demo.js
@@ -21,6 +21,14 @@
* JQuery on ready callback function
*/
function ready() {
+
+ // CSRF protection
+ $.ajaxSetup({
+ headers: {
+ 'csrf-token': $('meta[name="ct"]').attr('content')
+ }
+ });
+
// load all json data first
$.when(
$.ajax('/data/threshold_v0.1.1.json'),
diff --git a/server.js b/server.js
new file mode 100644
index 00000000..251977bd
--- /dev/null
+++ b/server.js
@@ -0,0 +1,15 @@
+#! /usr/bin/env node
+'use strict';
+
+if (process.env.GOOGLE_ANALYTICS){
+ process.env.GOOGLE_ANALYTICS = process.env.GOOGLE_ANALYTICS.replace(/\"/g,'');
+}
+// Deployment tracking
+require('cf-deployment-tracker-client').track();
+
+var server = require('./app');
+var port = process.env.PORT || process.env.VCAP_APP_PORT || 3000;
+
+server.listen(port, function () {
+ console.log('Server running on port: %d', port);
+});
diff --git a/test/test.express.js b/test/test.express.js
new file mode 100644
index 00000000..2c9f46a3
--- /dev/null
+++ b/test/test.express.js
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2015 IBM Corp. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+var app = require('../app');
+var request = require('supertest');
+var nock = require('nock');
+
+describe('express', function() {
+
+ it('load home page when GET /', function(done) {
+ request(app).get('/').expect(200, done);
+ });
+
+});
diff --git a/views/index.ejs b/views/index.ejs
index 43d95e62..2b942ef0 100755
--- a/views/index.ejs
+++ b/views/index.ejs
@@ -7,6 +7,7 @@
+
@@ -337,6 +338,15 @@
+