| 'use strict'; | 
|   | 
| const is = require('is-type-of'); | 
| const url = require('url'); | 
| const { JSONP_CONFIG } = require('../../lib/private_key'); | 
|   | 
| module.exports = { | 
|   /** | 
|    * return a middleware to enable jsonp response. | 
|    * will do some security check inside. | 
|    * @param  {Object} options jsonp options. can override `config.jsonp`. | 
|    * @return {Function} jsonp middleware | 
|    * @public | 
|    */ | 
|   jsonp(options) { | 
|     const defaultOptions = this.config.jsonp; | 
|     options = Object.assign({}, defaultOptions, options); | 
|     if (!Array.isArray(options.callback)) options.callback = [ options.callback ]; | 
|   | 
|     const csrfEnable = this.plugins.security && this.plugins.security.enable // security enable | 
|       && this.config.security.csrf && this.config.security.csrf.enable !== false // csrf enable | 
|       && options.csrf; // jsonp csrf enabled | 
|   | 
|     const validateReferrer = options.whiteList && createValidateReferer(options.whiteList); | 
|   | 
|     if (!csrfEnable && !validateReferrer) { | 
|       this.coreLogger.warn('[egg-jsonp] SECURITY WARNING!! csrf check and referrer check are both closed!'); | 
|     } | 
|     /** | 
|      * jsonp request security check, pass if | 
|      * | 
|      * 1. hit referrer white list | 
|      * 2. or pass csrf check | 
|      * 3. both check are disabled | 
|      * | 
|      * @param  {Context} ctx request context | 
|      */ | 
|     function securityAssert(ctx) { | 
|       // all disabled. don't need check | 
|       if (!csrfEnable && !validateReferrer) return; | 
|   | 
|       // pass referrer check | 
|       const referrer = ctx.get('referrer'); | 
|       if (validateReferrer && validateReferrer(referrer)) return; | 
|       if (csrfEnable && validateCsrf(ctx)) return; | 
|   | 
|       const err = new Error('jsonp request security validate failed'); | 
|       err.referrer = referrer; | 
|       err.status = 403; | 
|       throw err; | 
|     } | 
|   | 
|     return async function jsonp(ctx, next) { | 
|       const jsonpFunction = getJsonpFunction(ctx.query, options.callback); | 
|   | 
|       ctx[JSONP_CONFIG] = { | 
|         jsonpFunction, | 
|         options, | 
|       }; | 
|   | 
|       // before handle request, must do some security checks | 
|       securityAssert(ctx); | 
|   | 
|       await next(); | 
|   | 
|       // generate jsonp body | 
|       ctx.createJsonpBody(ctx.body); | 
|     }; | 
|   }, | 
| }; | 
|   | 
| function createValidateReferer(whiteList) { | 
|   if (!Array.isArray(whiteList)) whiteList = [ whiteList ]; | 
|   | 
|   return function(referrer) { | 
|     let parsed = null; | 
|     for (const item of whiteList) { | 
|       if (is.regExp(item) && item.test(referrer)) { | 
|         // regexp(/^https?:\/\/github.com\//): test the referrer with item | 
|         return true; | 
|       } | 
|   | 
|       parsed = parsed || url.parse(referrer); | 
|       const hostname = parsed.hostname || ''; | 
|   | 
|       if (item[0] === '.' && | 
|         (hostname.endsWith(item) || hostname === item.slice(1))) { | 
|         // string start with `.`(.github.com): referrer's hostname must ends with item | 
|         return true; | 
|       } else if (hostname === item) { | 
|         // string not start with `.`(github.com): referrer's hostname must strict equal to item | 
|         return true; | 
|       } | 
|     } | 
|   | 
|     return false; | 
|   }; | 
| } | 
|   | 
| function validateCsrf(ctx) { | 
|   try { | 
|     ctx.assertCsrf(); | 
|     return true; | 
|   } catch (_) { | 
|     return false; | 
|   } | 
| } | 
|   | 
| function getJsonpFunction(query, callbacks) { | 
|   for (const callback of callbacks) { | 
|     if (query[callback]) return query[callback]; | 
|   } | 
| } |