@edge-csrf/node-http
Version:
Edge-CSRF integration library for node's http module
7 lines (6 loc) • 7.36 kB
JavaScript
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});/*!
* cookie
* Copyright(c) 2012-2014 Roman Shtylman
* Copyright(c) 2015 Douglas Christopher Wilson
* MIT Licensed
*/var L=D,O=R,T=Object.prototype.toString,P=Object.prototype.hasOwnProperty,j=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,H=/^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/,N=/^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i,_=/^[\u0020-\u003A\u003D-\u007E]*$/;function D(e,r){if(typeof e!="string")throw new TypeError("argument str must be a string");var t={},n=e.length;if(n<2)return t;var i=r&&r.decode||B,o=0,a=0,s=0;do{if(a=e.indexOf("=",o),a===-1)break;if(s=e.indexOf(";",o),s===-1)s=n;else if(a>s){o=e.lastIndexOf(";",a-1)+1;continue}var c=p(e,o,a),f=m(e,a,c),d=e.slice(c,f);if(!P.call(t,d)){var l=p(e,a+1,s),u=m(e,s,l);e.charCodeAt(l)===34&&e.charCodeAt(u-1)===34&&(l++,u--);var h=e.slice(l,u);t[d]=$(h,i)}o=s+1}while(o<n);return t}function p(e,r,t){do{var n=e.charCodeAt(r);if(n!==32&&n!==9)return r}while(++r<t);return t}function m(e,r,t){for(;r>t;){var n=e.charCodeAt(--r);if(n!==32&&n!==9)return r+1}return t}function R(e,r,t){var n=t&&t.encode||encodeURIComponent;if(typeof n!="function")throw new TypeError("option encode is invalid");if(!j.test(e))throw new TypeError("argument name is invalid");var i=n(r);if(!H.test(i))throw new TypeError("argument val is invalid");var o=e+"="+i;if(!t)return o;if(t.maxAge!=null){var a=Math.floor(t.maxAge);if(!isFinite(a))throw new TypeError("option maxAge is invalid");o+="; Max-Age="+a}if(t.domain){if(!N.test(t.domain))throw new TypeError("option domain is invalid");o+="; Domain="+t.domain}if(t.path){if(!_.test(t.path))throw new TypeError("option path is invalid");o+="; Path="+t.path}if(t.expires){var s=t.expires;if(!U(s)||isNaN(s.valueOf()))throw new TypeError("option expires is invalid");o+="; Expires="+s.toUTCString()}if(t.httpOnly&&(o+="; HttpOnly"),t.secure&&(o+="; Secure"),t.partitioned&&(o+="; Partitioned"),t.priority){var c=typeof t.priority=="string"?t.priority.toLowerCase():t.priority;switch(c){case"low":o+="; Priority=Low";break;case"medium":o+="; Priority=Medium";break;case"high":o+="; Priority=High";break;default:throw new TypeError("option priority is invalid")}}if(t.sameSite){var f=typeof t.sameSite=="string"?t.sameSite.toLowerCase():t.sameSite;switch(f){case!0:o+="; SameSite=Strict";break;case"lax":o+="; SameSite=Lax";break;case"strict":o+="; SameSite=Strict";break;case"none":o+="; SameSite=None";break;default:throw new TypeError("option sameSite is invalid")}}return o}function B(e){return e.indexOf("%")!==-1?decodeURIComponent(e):e}function U(e){return T.call(e)==="[object Date]"}function $(e,r){try{return r(e)}catch{return e}}class v{constructor(r){this.domain="",this.httpOnly=!0,this.maxAge=void 0,this.name="_csrfSecret",this.partitioned=void 0,this.path="/",this.sameSite="strict",this.secure=!0,Object.assign(this,r)}}class g{constructor(r){this.fieldName="csrf_token",this.value=void 0,Object.assign(this,r),this._fieldNameRegex=new RegExp(`^(\\d+_)*${this.fieldName}$`)}}class C{constructor(r){this.excludePathPrefixes=[],this.ignoreMethods=["GET","HEAD","OPTIONS"],this.saltByteLength=8,this.secretByteLength=18,this.cookie=new v,this.token=new g;const t=r||{};if(t.cookie&&(t.cookie=new v(t.cookie)),t.token&&(t.token=new g(t.token)),Object.assign(this,t),this.saltByteLength<1||this.saltByteLength>255)throw Error("saltBytLength must be greater than 0 and less than 256");if(this.secretByteLength<1||this.secretByteLength>255)throw Error("secretBytLength must be greater than 0 and less than 256")}}const x=new g;function z(e){const r=new Uint8Array(e);return crypto.getRandomValues(r),r}function k(e){let r="";for(let t=0;t<e.byteLength;t+=1)r+=String.fromCharCode(e[t]);return btoa(r)}function b(e){let r;try{r=atob(e)}catch(n){if(n instanceof Error&&n.name==="InvalidCharacterError")return new Uint8Array;throw n}const t=new Uint8Array(r.length);for(let n=0;n<r.length;n+=1)t[n]=r.charCodeAt(n);return t}function M(e,r=x){for(const[t,n]of e.entries())if(r._fieldNameRegex.test(t))return n}async function V(e,r=x){if(r.value!==void 0)return r.value(e);const t=e.headers.get("x-csrf-token");if(t!==null)return t;const{fieldName:n}=r,i=e.headers.get("content-type")||"text/plain";if(i==="application/x-www-form-urlencoded"||i.startsWith("multipart/form-data")){const a=await e.formData(),s=M(a,r);return typeof s=="string"?s:""}if(i==="application/json"||i==="application/ld+json"){const s=(await e.json())[n];return typeof s=="string"?s:""}const o=await e.text();if(i.startsWith("text/plain"))try{const a=JSON.parse(o);if(!Array.isArray(a)||a.length===0)return o;const s=a[0],c=typeof s;return c==="string"?s:c==="object"?s[n]||"":s}catch{return o}return o}function F(e){const r=new Uint8Array(e);for(let t=0;t<e;t+=1)r[t]=Math.floor(Math.random()*255);return r}async function S(e,r){const t=new Uint8Array(e.byteLength+r.byteLength);t.set(e),t.set(r,e.byteLength);const n=await crypto.subtle.digest("SHA-1",t);return new Uint8Array(n)}async function I(e,r){const t=F(r),n=await S(e,t),i=new Uint8Array(2+r+n.byteLength);return i[0]=0,i[1]=r,i.set(t,2),i.set(n,r+2),i}async function W(e,r){if(e.byteLength<22)return!1;const t=e[1],n=e.subarray(2,2+t),i=e.subarray(2+t),o=await S(r,n);if(i.byteLength!==o.byteLength)return!1;for(let a=0;a<i.byteLength;a+=1)if(i[a]!==o[a])return!1;return!0}class E extends Error{}function G(e){const r=new C(e||{});return async t=>{const{request:n,url:i,getCookie:o,setCookie:a}=t;for(const d of r.excludePathPrefixes)if(i.pathname.startsWith(d))return;const s=o(r.cookie.name);let c;if(s===void 0?(c=z(r.secretByteLength),a({...r.cookie,value:k(c)})):c=b(s),!r.ignoreMethods.includes(n.method)){const d=await V(n,r.token);if(!await W(b(d),c))throw new E("csrf validation error")}const f=await I(c,r.saltByteLength);return k(f)}}function J(e){return new Promise((r,t)=>{const n=[],i=()=>{t(new Error("request aborted"))},o=f=>{n.push(f)},a=()=>{e.body=Buffer.concat(n),r(e.body.toString())},s=f=>{t(f)},c=()=>{e.removeListener("data",o),e.removeListener("end",a),e.removeListener("err",s),e.removeListener("aborted",i),e.removeListener("close",c)};e.on("aborted",i),e.on("data",o),e.on("end",a),e.on("err",s),e.on("close",c)})}class w extends g{constructor(r){super(r),this.responseHeader="X-CSRF-Token",Object.assign(this,r)}}class A extends C{constructor(r){super(r),this.excludePathPrefixes=[],this.token=new w;const t=r||{};t.token&&(t.token=new w(t.token)),Object.assign(this,t)}}function X(e){const r=new A(e),t=G(r);return async(n,i)=>{const o=L(n.headers.cookie||""),{url:a,headers:{host:s}}=n,c=new URL(`http://${s}${a||""}`),f=new Headers;Object.entries(n.headers).forEach(([u,h])=>{Array.isArray(h)?h.forEach(y=>f.append(u,y)):h!==void 0&&f.append(u,h)});const d=new Request(c,{method:n.method,headers:f,body:["GET","HEAD"].includes(n.method||"")?void 0:await J(n)}),l=await t({request:d,url:c,getCookie:u=>o[u],setCookie:u=>{const h=O(u.name,u.value,u),y=i.getHeader("Set-Cookie");Array.isArray(y)?i.setHeader("Set-Cookie",[...y,h]):typeof y=="string"?i.setHeader("Set-Cookie",[y,h]):i.setHeader("Set-Cookie",h)}});l&&i.setHeader(r.token.responseHeader,l)}}exports.CsrfError=E;exports.NodeHttpConfig=A;exports.NodeHttpTokenOptions=w;exports.createCsrfProtect=X;
;