Source: org/terraswarm/accessor/accessors/web/hosts/browser/demo/tutorial/tutorial.js

// Copyright (c) 2017 The Regents of the University of California.
// All rights reserved.
//
// Permission is hereby granted, without written agreement and without
// license or royalty fees, to use, copy, modify, and distribute this
// software and its documentation for any purpose, provided that the above
// copyright notice and the following two paragraphs appear in all copies
// of this software.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
// ENHANCEMENTS, OR MODIFICATIONS.

/**
 *  A browser host accessor tutorial.
 *
 *  @module @accessors-hosts/browser/demo/tutorial
 *  @author Beth Osyk
 *  @version $$Id$$
 */

$(document).ready(function () {
    var text;

    // Register listener for instantiate button.
    document.getElementById('button').addEventListener('click', function () {
        instantiateFunction();
    });

    var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
        gutters: ["CodeMirror-lint-markers"],
        lineNumbers: true,
        mode: 'javascript'
    });

    // Automatically instantiate the first time.
    text = editor.getValue();
    window.code = text;

    // Check for an accessor name, specified by @accessor.
    var regexp = /@accessor.*\n/;
    var index, index2;
    var id = "Tutorial";

    var matches = text.match(regexp);
    if (matches !== null && typeof matches !== 'undefined' &&
        matches.length > 0) {
        id = matches[0].substring(9).trim();
    }
    generateAccessorHTML(id, 'accessorbox', text);
	
    /** Instantiate the code in the editor textbox.  The function is named
     * instantiateFunction so as not to override any global instantiate().
     */
    var instantiateFunction = function () {
    
        var line;
        var text = editor.getValue();
        var errorNode;
        document.getElementById('accessorbox').innerHTML = text;
        var foundError = false;

        for (var i = 0; i < editor.lineCount(); i++) {
            line = editor.getLine(i);
            if (!checkInput(line)) {
                errorMarker = document.createElement('span');
                errorMarker.innerHTML = '*';
                errorMarker.className = "error";

                editor.setGutterMarker(i, "CodeMirror-lint-markers", errorMarker);
                foundError = true;
                break;
            }
        }


        if (!foundError) {
            editor.clearGutter('CodeMirror-lint-markers');
            document.getElementById('errorMessage').innerHTML = "";

            // TODO: Better error reporting.
            window.code = text;

            // Check for an accessor name, specified by @accessor.
            var regexp = /@accessor.*\n/;
            var index, index2;
            var id = "Tutorial";

            var matches = text.match(regexp);
            if (matches !== null && typeof matches !== 'undefined' &&
                matches.length > 0) {
                id = matches[0].substring(9).trim();
            }
            
            generateAccessorHTML(id, 'accessorbox', text);
        }
    }

});

/** Check input for malicious Javascript.
 * 
 * @param text Javascript to check for malicious content.
 * @returns false if unallowed Javascript is detected; true otherwise.
 */
function checkInput(text) {
    var errorMessage = document.getElementById('errorMessage');

    // Ensure input contains only allowed characters.
    // blacklist = ^whitelist
    // \w includes underscore.
    var blacklist = /[^\w\s{}\[\]().!=;'"|&*+%@,\/\\$:-]/;
    var matches = text.match(blacklist);
    if (matches !== null && typeof matches !== 'undefined' &&
        matches.length > 0) {
    	// Allow > and <
    	if (matches[0] !== '<' && matches[0] !== '>') {
    		console.log('matches ' + matches[0]);
    		errorMessage.innerHTML = 'Error: The ' + matches[0] +
            	' character is not permitted.';
    		return false;
    	}
    }

    // No loops. Prevent Denial of Service.
    blacklist = /for\s*{|forEach\s*{|while\s*{/;
    matches = text.match(blacklist);
    matches = text.match(blacklist);
    if (matches !== null && typeof matches !== 'undefined' &&
        matches.length > 0) {
        errorMessage.innerHTML = 'Error: Loops are not permitted.';
        return false;
    }

    // No access to window. or document. Prevent unauthorized use of user data.
    blacklist = /window.|document./;
    matches = text.match(blacklist);
    if (matches !== null && typeof matches !== 'undefined' &&
        matches.length > 0) {
        errorMessage.innerHTML = 'Error: Accessing ' + matches[0] +
            ' is not permitted.';
        return false;
    }

    // No regular expressions. Prevent Denial of Service.
    blacklist = /.match/;

    matches = text.match(blacklist);
    if (matches !== null && typeof matches !== 'undefined' &&
        matches.length > 0) {
        errorMessage.innerHTML = 'Error: Regular expressions are not permitted.';
        return false;
    }

    return true;
}

/** Override generateAccessorDocumentation to use comments in live code as the
 * documentation.
 * @path Not used in this implementation.
 * @id The name of the accessor.
 */
// TODO:  Add 'implements' and 'extends'.

function generateAccessorDocumentation(path, id) {
    window.accessorDocs = {};
    docs = {};

    // Parse any documentation.
    // Assume description goes until the end of the line.
    //var regexp = /@input|@output|@parameter/g;
    var regexp = /@input.*\n|@output.*\n|@parameter.*\n/g;
    var index, index2;

    var matches = window.code.match(regexp);
    if (matches !== null && typeof matches !== 'undefined') {
        matches.forEach(function (match) {

            index = match.indexOf(' ');
            if (index === -1) {
                index = match.indexOf('\t');
            }

            if (index !== -1) {
                index2 = match.indexOf(' ', index + 1);
                if (index2 === -1) {
                    index2 = match.indexOf('\t', index + 1);
                }

                if (index2 !== -1) {
                    var name = match.substring(index + 1, index2);
                    var description = match.substring(index2 + 1);
                    docs[name] = description;
                }
            }
        });
    }

    window.accessorDocs['accessorbox'] = docs;
}