Name

x_8821_code.Analysis

Description

var t = new x_8821_code.Analysis(); try{ gs.info(JSON.stringify(t.run(), , )); }catch(e){ gs.info(JSON.stringify(e)); }//

Script

var Analysis = Class.create();
Analysis.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
  /*
Server Side
- [] Avoid Global BR
- [] Before BR Should not insert or update records
- [] Avoid Eval
- [] Avoid Sysids in code
- [] BR should be wrapped in a function
- [] Avoid undocumented apis
- [] Check if script/code does not make use of foreign tables without GlideRecordSecure
- [] search transform scripts onbefore for .insert() .update() as they shouldnt do that.
- [] search code for the use of getResponse() as its a soap method that should be used.
- [] search transform maps where business rules are enabled and suggest to disable them.
Client Scripts
- [x] Avoid $
- [x] Avoid top
- [x] Avoid window
- [x] Avoid $j
- [x] Avoid jQuery
- [x] Avoid document
- [x] Use GlideAjax instead of GlideRecord
- [] g_form.field.getReference() Should use a callback
UI Policies
- [x] Avoid $
- [x] Avoid top
- [x] Avoid window
- [x] Avoid $j
- [x] Avoid jQuery
- [x] Avoid document
- [x] Use GlideAjax instead of GlideRecord
- [] g_form.field.getReference() Should use a callback

Tables to Check
[x]sys_script_include,
[x]sys_script_client,
[x]sys_script,

sys_processor,
sysevent_script_action,
sysauto_script,
sys_script_fix,

sys_ui_script

sp_widget,
sys_ui_page,
sys_ui_action,
  */
  run: function(){
  	var returnObj = {};
  	returnObj.businessRules = this._getBusinessRules();
  	returnObj.scriptIncludes = this._getScriptIncludes();
  	returnObj.uiPolicies = this._getUIPoliciesScripts();
  	returnObj.clientScripts = this._getClientScripts();
  	return returnObj;		
  },
  instanceCreateDate: (function(){
  	var properties = new GlideRecord('sys_properties');
  	if(properties.get('name','instance_name')){
  		return new GlideDateTime(properties.sys_created_on);
  	} else {
  		var gdt = new GlideDateTime();
  		gdt.addYearsLocalTime(-2);
  		return gdt;
  	}
  })(),
  instance:(function(){
  	return 'https://' + gs.getProperty('instance_name') + '.service-now.com/nav_to.do?uri=';
  })(),

  clientTests: [
  	/*{
  		regex: /^(function on)((.*)\n)+\}$/gm,
  		error: "Not wrapped in function, should be function on*(){}",
  		expect: true
  	},*/{
  		regex: /(\s)\$(\.|\()/gm,
  		error: "Do not use Prototype ($) as it's not supported on SP or Mobile",
  		expect: false
  	},{
  		regex: /(\s)\$j(\.|\()/gm,
  		error: "Do not use jQuery ($j) as it's not supported on SP or Mobile",
  		expect: false
  	},{
  		regex: /(\s)jQuery(\.|\()/gm,
  		error: "Do not use jQuery as it's not supported on SP or Mobile",
  		expect: false
  	},{
  		regex: /(\s)top(\.|\[)/gm,
  		error: "Do not use top as it's not supported on SP or Mobile",
  		expect: false
  	},{
  		regex: /(\s)window(\.|\[)/gm,
  		error: "Do not use window as it's not supported on SP or Mobile",
  		expect: false
  	},{
  		regex: /(\s)document(\.|\[)/gm,
  		error: "Do not use document as it's not supported on SP or Mobile",
  		expect: false
  	},{
  		regex: /(\s)eval\(/gm,
  		error: "Do not use eval, use bracket notation if possible",
  		expect: false
  	},{
  		regex: /new GlideRecord/gm,
  		error: "Use GlideAjax instead of GlideRecord",
  		expect: false
  	},{
  		regex: /\.getReference\(\)/gm,
  		error: "getReference should have a callback",
  		expect: false
  	},{
  		regex: /control./gm,
  		error: "Do not use control as it's not supported on SP or Mobile",
  		expect: false
  	},{
  		regex: /\.getXMLWait/gm,
  		error: "Do not use getXMLWait as it's not supported on SP or Mobile",
  		expect: false
  	},{
  		regex: /('|"|`)[a-f0-9]{32}('|"|`)/gm,
  		error: "Do not use hard-coded sys_ids",
  		expect: false
  	},
  ],
  serverTests: [
  	{
  		regex: /('|"|`)[a-f0-9]{32}('|"|`)/gm,
  		error: "Do not use hard-coded sys_ids",
  		expect: false
  	},{
  		regex: /(\s)eval\(/gm,
  		error: "Do not use eval",
  		expect: false
  	},{
  		regex: /(\s)\getRowCount\(/gm,
  		error: "GlideAggregate should be used instead of getRowCount",
  		expect: false
  	}/*,{
  			regex: /(\s)\GlideRecord\(/gm,
  			error: "GlideRecordSecure should be used when possible",
  			expect: false
  		}*/
  ],

  liveCheck: function(){

  	var inputObj = JSON.parse(this.getParameter('sysparm_obj'));
  	var returnObj = {
  		from:"server",
  		input: inputObj,
  		errors: []
  	};
  	try{
  		returnObj.serverTest = inputObj.type === 'server';
  		returnObj.clientTest = inputObj.type === 'client';
  		if(returnObj.serverTest){
  			this.serverTests.forEach(function(test){
  				var passes = test.regex.test(inputObj.script);
  				var result = passes == test.expect;
  				returnObj.errors.push({"test": test,"passed":result});
  			});

  		}
  		if(returnObj.clientTest){
  			this.clientTests.forEach(function(test){
  				var passes = test.regex.test(inputObj.script);
  				var result = passes == test.expect;
  				returnObj.errors.push({"test": test,"passed":result});
  			});
  		}
  	}catch(err){
  		returnObj.err = err;
  	}
  	return JSON.stringify(returnObj);
  },
  _search: function(term){
  	var returnArr = [];
  	var cs = new sn_codesearch.CodeSearch();
  	cs.setSearchAllScopes(true);//search all scopes
  	cs.setLimit(1000);
  	//cs.setSearchTable('sys_script');//business rule
  	var results = cs.search(term);
  	results.forEach(function(result){
  		if(result.hits.length>0){
  			var returnObj = {};
  			result.hits.forEach(function(hit){
  				returnObj.table = hit.className;
  				returnObj.name = hit.name;
  				returnObj.matches = hit.matches;
  			});
  			returnArr.push(returnObj);
  		}
  	});
  	return returnArr;
  },
  _getServerScripts: function(table){
  	var returnArr = [];
  	var scriptFields = [];
  	var dictionaryGR = new GlideRecord('sys_dictionary');
  	dictionaryGR.addEncodedQuery('internal_type=script^ORinternal_type=script_plain^ORinternal_type=script_server');
  	while(dictionaryGR.next()){
  		scriptFields.push(dictionaryGR.element + '');
  	}
  	var tableGR = new GlideRecord(table);
  	if(tableGR.isValidField('sys_updated_on')){
  		tableGR.addQuery('sys_updated_on','>', this.instanceCreateDate);//exclude these as mainy oob
  	}
  	if(tableGR.isValidField('active')){
  		tableGR.addQuery('active','true');
  	}
  	tableGR.query();
  	while(tableGR.next()){
  		scriptFields.forEach(function(field){
  			var script = record.getValue(field)||'';
  			this.serverTests.forEach(function(test){
  				var passes = test.regex.test(script);
  				if(passes != test.expect){
  					errors.push(test.error);
  				}
  			});
  		});
  	}
  },
  _getBusinessRules: function(){
  	/*
  	- [x] Avoid Global BR
  	- [x] Before BR Should not insert or update records
  	- [x] Avoid Eval
  	- [x] Avoid Sysids in code
  	- [x] BR should be wrapped in a function
  	- [] Avoid undocumented apis
  	- [] Check if script/code does not make use of foreign tables without GlideRecordSecure
  	- [] search code for the use of getResponse() as its a soap method that should be used.
  	*/
  	var returnArr = [];
  	var tests = this.serverTests.concat([{
  		regex: /^(function)((.*)\n)+}$/gm,
  		error: "Not wrapped in function",
  		expect: true
  	}]);
  	var br = new GlideRecord('sys_script');
  	if(arguments[0]){
  		br.addQuery('sys_id', arguments[0]);
  	} else {
  		br.addQuery('active','true');
  		br.addQuery('sys_updated_on','>', this.instanceCreateDate);//exclude these as mainy oob
  	}
  	br.query();
  	while(br.next()){
  		var script = br.getValue('script') || "";
  		var errors = [];
  		if(br.getValue('collection') === 'global'){
  			errors.push('Avoid Global Business Rules');
  		}
  		if(br.getValue('when') === 'before'){
  			if(script.indexOf('insert()')>=0){
  				errors.push('Avoid insert() in before business rules');
  			}
  			if(script.indexOf('update()')>=0){
  				errors.push('Avoid update() in before business rules');
  			}
  		}
  		this.serverTests.forEach(function(test){
  			var passes = test.regex.test(script);
  			if(passes != test.expect){
  				errors.push(test.error);
  			}
  		});
  		if(errors.length>0){
  			returnArr.push({
  				name: br.getValue('name'),
  				sys_id: br.getValue('sys_id'),
  				updated_by: br.getValue('sys_updated_by'),
  				link: this.instance + br.getValue('sys_class_name') + '.do?sys_id=' + br.getValue('sys_id'),
  				errors: errors
  			});
  		}
  	}
  	return returnArr;
  },
  _getScriptIncludes: function(){
  	/*
  	- [x] Avoid Eval
  	- [x] Avoid Sysids in code
  	- [] Avoid undocumented apis
  	- [] Check if script/code does not make use of foreign tables without GlideRecordSecure
  	- [] search code for the use of getResponse() as its a soap method that should be used.
  	*/
  	var returnArr = [];
  	var si = new GlideRecord('sys_script_include');
  	if(arguments[0]){
  		si.addQuery('sys_id', arguments[0]);
  	} else {
  		si.addQuery('active','true');
  		si.addQuery('sys_updated_on','>', this.instanceCreateDate);//exclude these as mainy oob
  	}
  	si.query();
  	while(si.next()){
  		var script = si.getValue('script') || "";
  		var errors = [];
  		this.serverTests.forEach(function(test){
  			var passes = test.regex.test(script);
  			if(passes != test.expect){
  				errors.push(test.error);
  			}
  		});
  		if(errors.length>0){
  			returnArr.push({
  				name: si.getValue('name'),
  				sys_id: si.getValue('sys_id'),
  				updated_by: si.getValue('sys_updated_by'),
  				link: this.instance + si.getValue('sys_class_name') + '.do?sys_id=' + si.getValue('sys_id'),
  				errors: errors
  			});
  		}
  	}
  	return returnArr;
  },
  _getUIPoliciesScripts: function(){
  	var returnArr = [];
  	var up = new GlideRecord('sys_ui_policy');
  	if(arguments[0]){
  		up.addQuery('sys_id', arguments[0]);
  	} else {
  		up.addQuery('active','true');
  		up.addQuery('run_scripts','true');
  		up.addQuery('sys_updated_on','>', this.instanceCreateDate);//exclude these as mainy oob
  	}
  	up.query();
  	while(up.next()){
  		var script_true = up.getValue('script_true') || "";
  		var script_false = up.getValue('script_false') || "";
  		var errors = [];
  		this.clientTests.forEach(function(test){
  			var passes1 = test.regex.test(script_true);
  			var passes2 = test.regex.test(script_false);
  			if(passes1 != test.expect || passes2 != test.expect){
  				errors.push(test.error);
  			}
  		});
  		if(errors.length>0){
  			returnArr.push({
  				name: up.getValue('short_description'),
  				sys_id: up.getValue('sys_id'),
  				updated_by: up.getValue('sys_updated_by'),
  				link: this.instance + up.getValue('sys_class_name') + '.do?sys_id=' + up.getValue('sys_id'),
  				errors: errors
  			});
  		}
  	}
  	return returnArr;
  },
  _getClientScripts: function(){
  	var returnArr = [];
  	var cs = new GlideRecord('sys_script_client');
  	if(arguments[0]){
  		cs.addQuery('sys_id', arguments[0]);
  	} else {
  		cs.addQuery('active','true');
  		cs.addQuery('sys_updated_on','>', this.instanceCreateDate);//exclude these as mainy oob
  	}
  	cs.query();
  	while (cs.next()) {   // iterate through records
  		var script = cs.getValue('script') || "";
  		var errors = [];
  		this.clientTests.forEach(function(test){
  			var passes = test.regex.test(script);
  			if(passes != test.expect){
  				errors.push(test.error);
  			}
  		});
  		if(errors.length>0){
  			returnArr.push({
  				name: cs.getValue('name'),
  				sys_id: cs.getValue('sys_id'),
  				updated_by: cs.getValue('sys_updated_by'),
  				link: this.instance + cs.getValue('sys_class_name') + '.do?sys_id=' + cs.getValue('sys_id'),
  				errors: errors
  			});
  		}
  	}
  	return returnArr;
  },
  type: 'Analysis'
});

Sys ID

c5c833f54f55f700c660b1d18110c78e

Offical Documentation

Official Docs: