Advanced linter check scripts
Summarize
Summary of Advanced linter check scripts
Advanced linter check scripts in ServiceNow enable customers to write custom checks that analyze script content for potential issues. These checks operate on an abstract syntax tree (AST) representation of the script, allowing detailed inspection of code structure and usage patterns, such as detecting deprecated API calls or inefficient code constructs.
Show less
Key Features
- AST-based analysis: Linter checks use the
rootNodeobject representing the root of the script’s AST, enabling traversal and inspection of every code element. - Node visitation: The
visitfunction onrootNodeiterates over each node in the AST, invoking a callback function for inspection. Returningfalsein the callback stops further traversal. - Node inspection functions: A rich set of node functions is available for detailed analysis, including:
getTypeName()to identify node types (e.g., function calls as "CALL")getNameIdentifier()to retrieve names of variables or functions- Location functions like
getLineNo(),getColumnNo(), andgetAbsolutePosition()for pinpointing code issues - Structural functions like
getParent()andgetRootNode()to navigate the AST hierarchy debugPrint()to output a readable representation of the AST for debugging
- Example usage: A sample script demonstrates detecting a deprecated function named
badFunction()by scanning nodes of type "NAME" with the matching identifier, flagging occurrences as findings.
Practical Application for ServiceNow Customers
By leveraging advanced linter check scripts, ServiceNow customers can proactively enforce coding standards and identify problematic patterns in server-side scripts, client scripts, or business rules. This improves code quality, performance, and maintainability by catching deprecated APIs, inefficient loops, or overly complex logic before deployment.
Using the provided node API, customers can customize their linting rules precisely to their organizational coding guidelines and integrate these checks into automated scans of script-containing records.
Linter check scripts helps you in writing checks that look for issues in scripts. When a linter check is run on a record, it provides an abstract syntax tree for its code. You can use this abstract syntax tree to analyze issues with the code such as too many nested if statements or usages of slow API in a while loop.
Linter check usage
Linter checks have a unique object to use called rootNode. This is the root
node of the parsed abstract syntax tree (AST) for the script of the current record. This object
has many functions, but the visit function is the most important. The
visit function takes a callback function as a parameter which gets called on
every node of the tree. The callback function then takes a node as a parameter which represents
the current node during its iteration. You can return false from the callback function to stop
iterating the tree early, otherwise it keeps visiting every subnode in the tree of the node you
called visit on.
badFunction().
You can write a Linter Check with a script in the following
example:(function(engine) {
engine.rootNode.visit(function(node) {
if (node.getTypeName() === "NAME" &&
node.getNameIdentifier() === "badFunction" &&
node.getParent().getTypeName() === "CALL") {
engine.finding.incrementWithNode(node);
}
});
})(engine);When
you run a scan with this Linter Check, it checks every record in the scan with a script field.
For example, if a record in that scan has a script that looks like the following, the scan picks
up a finding for this record./*
badFunction()
*/
// badFunction()
function badFunction() {
return;
}
var GoodClass = Class.create();
GoodClass.prototype = Object.extendsObject(Object, {
badFunction: function() {
// actually good
}
});
var badFunction = ["badFunction", "badFunction()"];
badFunctionButSometimesGood();
badFunction();
Node functions
getRootNode(): Get the root node of the tree for this nodegetParent(): Get the parent of this nodegetLineNo(): Get the line number location for this nodegetColumnNo(): Get the column number location for this nodetoSource(): Get the source for this node. The source is based on this node and its childrengetTypeName(): Get the node type name of this node. For example, a function call in source is tokenized as a node with the type name of "CALL"Note:The values ofnode.getTypeName()are from different types of nodes in an Abstract Syntax Tree (AST). The possible values come from Rhino's AST parser itself.getNameIdentifier(): If the node type of this node is "NAME", then return the identifier, which is the string value of the name itself. A "NAME" node represents a simple name that is not a keyword, like a function name or a variable name.Note:If this node is not a NAME node, then the result is null.getAbsolutePosition(): Get the absolute position of this node. The absolute position is the number of characters from the start of the script to this nodecompareTo(other): Compare this node with another node. The node with a greater absolute position and length will be the larger in comparison.otheris a node to compare with this nodevisit(callbackFunction): Visit each node in the subtree starting from this node and execute the given callback function on each node.callbackFunctionis a function that will be executed on each node in the subtree of this node. This callback function takes a LinterCheckAstNode as a parameter which is will be the node being visiteddebugPrint(): Returns a string representation of the abstract syntax tree, starting from this node. Each line contains information about a node, and the indentation represents the hierarchy relationship between the nodes. The information in each line is arranged as follows- Absolute position
- Node type name
- Position relative to parent
- Length
- Name identifier (if a NAME node)