UNPKG

blackbird-server

Version:
513 lines (512 loc) 16.5 kB
<!doctype html> <html lang="en"> <head> <title>Code coverage report for modules/middleware/session.js</title> <meta charset="utf-8" /> <link rel="stylesheet" href="../../prettify.css" /> <link rel="stylesheet" href="../../base.css" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type='text/css'> .coverage-summary .sorter { background-image: url(../../sort-arrow-sprite.png); } </style> </head> <body> <div class='wrapper'> <div class='pad1'> <h1> <a href="../../index.html">all files</a> / <a href="index.html">modules/middleware/</a> session.js </h1> <div class='clearfix'> <div class='fl pad1y space-right2'> <span class="strong">90.38% </span> <span class="quiet">Statements</span> <span class='fraction'>47/52</span> </div> <div class='fl pad1y space-right2'> <span class="strong">72.5% </span> <span class="quiet">Branches</span> <span class='fraction'>29/40</span> </div> <div class='fl pad1y space-right2'> <span class="strong">100% </span> <span class="quiet">Functions</span> <span class='fraction'>9/9</span> </div> <div class='fl pad1y space-right2'> <span class="strong">90.38% </span> <span class="quiet">Lines</span> <span class='fraction'>47/52</span> </div> </div> </div> <div class='status-line high'></div> <pre><table class="coverage"> <tr><td class="line-count quiet">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150</td><td class="line-coverage quiet"><span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes">21×</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16×</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">13×</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes"></span> <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const mach = require("../index"); const Promise = require("../utils/Promise"); const decodeBase64 = require("../utils/decodeBase64"); const encodeBase64 = require("../utils/encodeBase64"); const makeHash = require("../utils/makeHash"); const CookieStore = require("./session/CookieStore"); const {is} = require("ramda"); mach.extend( require("../extensions/server") ); &nbsp; /** * The maximum size of an HTTP cookie. */ const MAX_COOKIE_SIZE = 4096; &nbsp; /** * Stores the given session and returns a promise for a value that should be stored * in the session cookie to retrieve the session data again on the next request. */ function encodeSession(session, store, secret) { return store.save(session).then(function (data) { const cookie = encodeBase64(`${data}--${makeHashWithSecret(data, secret)}`); &nbsp; <span class="missing-if-branch" title="if path not taken" >I</span>if (cookie.length &gt; MAX_COOKIE_SIZE) { <span class="cstat-no" title="statement not covered" > throw new Error("Cookie data size exceeds 4kb; content dropped");</span> } &nbsp; return cookie; }); } &nbsp; /** * Decodes the given cookie value and returns a promise for the corresponding session * data from the store. Also verifies the hash value to ensure the cookie has not been * tampered with. If it has, returns null. */ function decodeCookie(cookie, store, secret) { const value = decodeBase64(cookie); const index = value.lastIndexOf("--"); const data = value.substring(0, index); const hash = value.substring(index + 2); &nbsp; // Verify the cookie has not been tampered with. <span class="missing-if-branch" title="else path not taken" >E</span>if (hash === makeHashWithSecret(data, secret)) { return store.load(data); } &nbsp; <span class="cstat-no" title="statement not covered" > return null;</span> } &nbsp; function makeHashWithSecret(data, secret) { return makeHash(secret ? data + secret : <span class="branch-1 cbranch-no" title="branch not covered" >data)</span>; } &nbsp; /** * A middleware that provides support for HTTP sessions using cookies. * * Options may be any of the following: * * - secret A cryptographically secure secret key that will be used to verify * the integrity of session data that is received from the client * - name The name of the cookie. Defaults to "_session" * - path The path of the cookie. Defaults to "/" * - domain The cookie's domain. Defaults to null * - secure True to only send this cookie over HTTPS. Defaults to false * - expireAfter The number of seconds after which sessions expire. Defaults * to 0 (no expiration) * - httpOnly True to restrict access to this cookie to HTTP(S) APIs. * Defaults to true * - store An instance of MemoryStore, CookieStore, or RedisStore that * is used to store session data. Defaults to a new CookieStore * * Example: * * app.use(mach.session, { * secret: 'the-secret', * secure: true * }); * * Hint: A great way to generate a cryptographically secure session secret from * the command line: * * $ node -p "require('crypto').randomBytes(64).toString('hex')" * * Note: Since cookies are only able to reliably store about 4k of data, if the * session cookie payload exceeds that the session will be dropped. */ function session(app, options) { options = options || <span class="branch-1 cbranch-no" title="branch not covered" >{};</span> &nbsp; <span class="missing-if-branch" title="if path not taken" >I</span>if (is(String, options)) { <span class="cstat-no" title="statement not covered" > options = {secret: options};</span> } &nbsp; const secret = options.secret; const name = options.name || "_session"; const path = options.path || "/"; const domain = options.domain; const expireAfter = options.expireAfter || 0; const httpOnly = "httpOnly" in options ? <span class="branch-0 cbranch-no" title="branch not covered" >options.httpOnly || false </span>: true; const secure = options.secure || false; const store = options.store || new CookieStore(options); &nbsp; <span class="missing-if-branch" title="if path not taken" >I</span>if (!secret) { <span class="cstat-no" title="statement not covered" > console.warn([</span> "WARNING: There was no \"secret\" option provided to mach.session! This poses", "a security vulnerability because session data will be stored on clients without", "any server-side verification that it has not been tampered with. It is strongly", "recommended that you set a secret to prevent exploits that may be attempted using", "carefully crafted cookies." ].join("\n")); } &nbsp; return function (conn) { <span class="missing-if-branch" title="if path not taken" >I</span>if (conn.session) { <span class="cstat-no" title="statement not covered" > return conn.call(app); </span>// Don't overwrite the existing session. } &nbsp; const cookie = conn.request.cookies[name]; &nbsp; return Promise.resolve(cookie &amp;&amp; decodeCookie(cookie, store, secret)).then(function (object) { conn.session = object || {}; &nbsp; return conn.call(app).then(function () { return Promise.resolve(conn.session &amp;&amp; encodeSession(conn.session, store, secret)).then(function (newCookie) { const expires = expireAfter &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >new Date(Date.now() + expireAfter * 1000);</span> &nbsp; // Don't bother setting the cookie if its value // hasn't changed and there is no expires date. if (newCookie === cookie &amp;&amp; !expires) { return; } &nbsp; conn.response.setCookie(name, { value: newCookie, path, domain, expires, httpOnly, secure }); }, conn.onError); }); }, conn.onError); }; } &nbsp; module.exports = session; &nbsp;</pre></td></tr> </table></pre> <div class='push'></div><!-- for sticky footer --> </div><!-- /wrapper --> <div class='footer quiet pad2 space-top1 center small'> Code coverage generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Wed Jul 06 2016 20:37:18 GMT-0500 (CDT) </div> </div> <script src="../../prettify.js"></script> <script> window.onload = function () { if (typeof prettyPrint === 'function') { prettyPrint(); } }; </script> <script src="../../sorter.js"></script> </body> </html>