Using AST Code Scanning to Stop Financial Losses in Frontend 618 Events
This article explains how the Taobao front‑end team leveraged Abstract Syntax Tree (AST) analysis with Babel to automatically detect risky code patterns—such as default price assignments, improper calculations, and hard‑coded promotional messages—during the 618 promotion, thereby preventing financial loss and public backlash.
The 2020 618 promotion is a major sales event for Taobao, and the front‑end team needed reliable ways to ensure the promotion runs smoothly while preventing financial loss. This article, written by the channel and D2C intelligence team, describes how they used static code scanning based on AST to detect risky code before it reaches production.
1. Introduction
During large‑scale promotions, asset loss (资损) prevention becomes critical. Traditional code review and real‑time monitoring can only mitigate loss after it occurs, and manual reviews are costly and inconsistent. The goal is to automatically discover potential loss‑inducing code patterns at commit time.
2. What is AST
In computer science, an Abstract Syntax Tree (AST) is an abstract representation of the syntactic structure of source code. Each node in the tree corresponds to a construct occurring in the source code.
An example of an AST for a simple initialization statement is shown below.
Tools like
astexplorerlet you visualize how the code
var str = "hello world"is broken into an AST.
The Babel compiler also relies on AST: source code is parsed into an AST, transformed, and then generated back to JavaScript (e.g., converting ES6 to ES5). The three stages are parse → transform → generate .
3. Implementing the Solution
3.1 Finding "price" variables
Price‑related variables are high‑risk. By matching identifier names that end with
price(e.g.,
xxxPrice) using a simple regular expression, we can locate them in the AST.
<code>const WHITE_LIST = ['price']; // TODO: extend
const PRICE_REG = new RegExp(WHITE_LIST.map(s => s + '$').join('|'), 'i');</code>When traversing the AST, we check
Identifiernodes against this regex.
<code>const isPrice = str => PRICE_REG.test(str);
const visitor = {
Identifier(path) {
const {id} = path.node;
if (isPrice(id.name)) {
// price variable matched!
}
}
};</code>3.2 Detecting default price assignments
Three common patterns are handled:
Direct assignment:
const price = 10;ES6 destructuring with default:
const {price = 10} = data;Logical OR default:
const price = data.price || 10;For direct assignments we look for
VariableDeclaratornodes where the identifier matches a price name and the initializer is a positive numeric literal.
<code>const t = require('@babel/types');
const visitor = {
VariableDeclarator(path) {
const {id, init} = path.node;
if (t.isIdentifier(id) && isPrice(id.name) && t.isNumericLiteral(init) && init.value > 0) {
// direct default price matched!
}
}
};</code>For destructuring we target
AssignmentPatternnodes, and for the logical OR case we traverse
LogicalExpressionnodes looking for the
||operator with a positive numeric right‑hand side.
<code>VariableDeclarator(path) {
const {id, init} = path.node;
if (t.isIdentifier(id) && isPrice(id.name)) {
path.traverse({
LogicalExpression(subPath) {
const {operator, right} = subPath.node;
if (operator === '||' && t.isNumericLiteral(right) && right.value > 0) {
// "||" default price matched!
}
}
});
}
}</code>3.3 Variable tracking across function calls
Simple identifier matching fails when a price variable is passed as an argument to a function. By using Babel's
scopeand
bindinginformation, we can trace the relationship between arguments and parameters, then inspect the function body for price‑related calculations.
<code>const Helper = {
findScope(path, matchFunc) {
let scope = path.scope;
while (scope && !matchFunc(scope)) {
scope = scope.parent;
}
return scope;
}
};
const checkPriceCalcVisitor = {
CallExpression(path) {
const {arguments: args, callee: {name}} = path.node;
const priceIdx = args.findIndex(arg => isPrice(arg));
if (priceIdx === -1) return;
const foundFunc = Helper.findScope(path, scope => {
const binding = scope.bindings[name];
return binding && t.isFunctionDeclaration(binding.path.node);
});
if (!foundFunc) return;
const funcPath = foundFunc.bindings[name].path;
const {params} = funcPath.node;
const param = params[priceIdx];
if (!t.isIdentifier(param)) return;
const renamed = param.name;
const {referencePaths = []} = funcPath.scope.bindings[renamed] || {};
referencePaths.forEach(refPath => {
refPath.getStatementParent().traverse(checkPriceCalcVisitor);
});
}
};</code>4. Detection Results
After deploying the scanner, about 1/7 of the surveyed repositories triggered the rules. Sample risky snippets include:
Default price in destructuring:
let { rPrice = 1 } = res.data || {};Hard‑coded mock price in component props:
itemPrice: '99'Hook default value exposing a monetary amount:
const [price, setPrice] = useState(50);Inline discount calculation that should be server‑side:
let discount = Math.ceil(100 * (price1 / 1) / (price2 / 1)) / 10;Promotional toast containing the keyword "双11" during the 618 event.
These findings demonstrate that AST‑based static analysis can effectively surface hidden financial‑loss and public‑opinion risks in front‑end code, though business‑specific scenarios may still require additional strategies.
Future work will focus on extending rule coverage to more complex business logic and improving detection precision.
Taobao Frontend Technology
The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.