auto-cr-rules 2.0.63 → 2.0.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.missingAsyncCleanup = void 0;
|
|
4
|
-
var types_1 = require("../types");
|
|
5
|
-
var isIdentifier = function (node, name) {
|
|
6
|
-
return node.type === 'Identifier' && node.value === name;
|
|
7
|
-
};
|
|
8
|
-
var isMemberExpressionWithIdentifier = function (node, name) {
|
|
9
|
-
if (node.type !== 'MemberExpression') {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
if (node.property.type === 'Identifier' && node.property.value === name) {
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
return false;
|
|
16
|
-
};
|
|
17
|
-
var createMetrics = function () { return ({
|
|
18
|
-
setInterval: {
|
|
19
|
-
api: 'setInterval',
|
|
20
|
-
cleanup: 'clearInterval',
|
|
21
|
-
calls: [],
|
|
22
|
-
cleanupCount: 0,
|
|
23
|
-
codeSample: 'setInterval(() => {/* ... */}, delay)',
|
|
24
|
-
},
|
|
25
|
-
addEventListener: {
|
|
26
|
-
api: 'addEventListener',
|
|
27
|
-
cleanup: 'removeEventListener',
|
|
28
|
-
calls: [],
|
|
29
|
-
cleanupCount: 0,
|
|
30
|
-
codeSample: 'target.addEventListener("type", handler)',
|
|
31
|
-
},
|
|
32
|
-
}); };
|
|
33
|
-
function isTargetedCall(callee, name) {
|
|
34
|
-
return isIdentifier(callee, name) || isMemberExpressionWithIdentifier(callee, name);
|
|
35
|
-
}
|
|
36
|
-
function visit(node, cb) {
|
|
37
|
-
var _a;
|
|
38
|
-
if (!node) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (Array.isArray(node)) {
|
|
42
|
-
node.forEach(function (child) { return visit(child, cb); });
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (typeof node !== 'object') {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
var candidate = node;
|
|
49
|
-
if (candidate.type === 'CallExpression') {
|
|
50
|
-
cb(candidate);
|
|
51
|
-
var call = candidate;
|
|
52
|
-
visit(call.callee, cb);
|
|
53
|
-
if (call.arguments) {
|
|
54
|
-
for (var _i = 0, _b = call.arguments; _i < _b.length; _i++) {
|
|
55
|
-
var arg = _b[_i];
|
|
56
|
-
visit((_a = arg.expression) !== null && _a !== void 0 ? _a : arg, cb);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (call.typeArguments) {
|
|
60
|
-
visit(call.typeArguments, cb);
|
|
61
|
-
}
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
for (var _c = 0, _d = Object.values(candidate); _c < _d.length; _c++) {
|
|
65
|
-
var value = _d[_c];
|
|
66
|
-
visit(value, cb);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
exports.missingAsyncCleanup = (0, types_1.defineRule)('require-cleanup-for-async-effects', { tag: 'base', severity: types_1.RuleSeverity.Warning }, function (_a) {
|
|
70
|
-
var ast = _a.ast, helpers = _a.helpers, language = _a.language, messages = _a.messages;
|
|
71
|
-
var metrics = createMetrics();
|
|
72
|
-
visit(ast, function (call) {
|
|
73
|
-
var callee = call.callee;
|
|
74
|
-
if (isTargetedCall(callee, 'setInterval')) {
|
|
75
|
-
metrics.setInterval.calls.push(call);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if (isTargetedCall(callee, 'clearInterval')) {
|
|
79
|
-
metrics.setInterval.cleanupCount += 1;
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
if (isTargetedCall(callee, 'addEventListener')) {
|
|
83
|
-
metrics.addEventListener.calls.push(call);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
if (isTargetedCall(callee, 'removeEventListener')) {
|
|
87
|
-
metrics.addEventListener.cleanupCount += 1;
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
var suggestionsByLanguage = language === 'zh'
|
|
92
|
-
? [
|
|
93
|
-
{ text: '在 useEffect 的返回函数或组件卸载时调用对应的清理函数。' },
|
|
94
|
-
{ text: '确保自定义 Hook 在调用组件卸载时执行 clearInterval/removeEventListener。' },
|
|
95
|
-
]
|
|
96
|
-
: [
|
|
97
|
-
{ text: 'Return a cleanup function from useEffect to clear timers or listeners.' },
|
|
98
|
-
{ text: 'Ensure custom hooks expose a teardown that calls clearInterval/removeEventListener.' },
|
|
99
|
-
];
|
|
100
|
-
Object.values(metrics).forEach(function (metric) {
|
|
101
|
-
if (metric.calls.length === 0 || metric.cleanupCount > 0) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
var description = messages.missingAsyncCleanup({ api: metric.api, cleanup: metric.cleanup });
|
|
105
|
-
var firstCall = metric.calls[0];
|
|
106
|
-
helpers.reportViolation({
|
|
107
|
-
description: description,
|
|
108
|
-
code: metric.codeSample,
|
|
109
|
-
suggestions: suggestionsByLanguage,
|
|
110
|
-
span: firstCall.span,
|
|
111
|
-
}, firstCall.span);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noUnsafeDangerouslySetInnerHTML = void 0;
|
|
4
|
-
var types_1 = require("../types");
|
|
5
|
-
var ATTRIBUTE_NAME = 'dangerouslySetInnerHTML';
|
|
6
|
-
var HTML_KEY = '__html';
|
|
7
|
-
var isIdentifierAttribute = function (attribute, name) {
|
|
8
|
-
return attribute.name.type === 'Identifier' && attribute.name.value === name;
|
|
9
|
-
};
|
|
10
|
-
var isTrustedLiteral = function (expression) {
|
|
11
|
-
if (expression.type === 'StringLiteral') {
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
if (expression.type === 'TemplateLiteral') {
|
|
15
|
-
var template = expression;
|
|
16
|
-
return template.expressions.length === 0;
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
};
|
|
20
|
-
var extractHtmlExpression = function (attribute) {
|
|
21
|
-
if (!attribute.value) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
if (attribute.value.type === 'StringLiteral') {
|
|
25
|
-
return attribute.value;
|
|
26
|
-
}
|
|
27
|
-
if (attribute.value.type !== 'JSXExpressionContainer') {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
var container = attribute.value;
|
|
31
|
-
var expression = container.expression;
|
|
32
|
-
if (!expression || expression.type === 'JSXEmptyExpression') {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
if (expression.type === 'ObjectExpression') {
|
|
36
|
-
for (var _i = 0, _a = expression.properties; _i < _a.length; _i++) {
|
|
37
|
-
var property = _a[_i];
|
|
38
|
-
if (property.type !== 'KeyValueProperty') {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
var key = property.key;
|
|
42
|
-
var keyName = key.type === 'Identifier'
|
|
43
|
-
? key.value
|
|
44
|
-
: key.type === 'StringLiteral'
|
|
45
|
-
? key.value
|
|
46
|
-
: null;
|
|
47
|
-
if (keyName === HTML_KEY) {
|
|
48
|
-
return property.value;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return expression;
|
|
52
|
-
}
|
|
53
|
-
return expression;
|
|
54
|
-
};
|
|
55
|
-
var visit = function (node, callback) {
|
|
56
|
-
if (!node) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (Array.isArray(node)) {
|
|
60
|
-
node.forEach(function (child) { return visit(child, callback); });
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (typeof node !== 'object') {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
var candidate = node;
|
|
67
|
-
if (candidate.type === 'JSXAttribute') {
|
|
68
|
-
callback(candidate);
|
|
69
|
-
}
|
|
70
|
-
for (var _i = 0, _a = Object.values(candidate); _i < _a.length; _i++) {
|
|
71
|
-
var value = _a[_i];
|
|
72
|
-
visit(value, callback);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
exports.noUnsafeDangerouslySetInnerHTML = (0, types_1.defineRule)('no-unsafe-dangerously-set-inner-html', { tag: 'security', severity: types_1.RuleSeverity.Error }, function (_a) {
|
|
76
|
-
var ast = _a.ast, helpers = _a.helpers, language = _a.language, messages = _a.messages;
|
|
77
|
-
visit(ast, function (attribute) {
|
|
78
|
-
if (!isIdentifierAttribute(attribute, ATTRIBUTE_NAME)) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
var expression = extractHtmlExpression(attribute);
|
|
82
|
-
if (expression && isTrustedLiteral(expression)) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
var suggestions = language === 'zh'
|
|
86
|
-
? [
|
|
87
|
-
{ text: '优先使用经过消毒的 HTML(例如 DOMPurify.sanitize)。' },
|
|
88
|
-
{ text: '避免直接渲染来自外部或用户输入的字符串。' },
|
|
89
|
-
]
|
|
90
|
-
: [
|
|
91
|
-
{ text: 'Sanitize the HTML string before passing it in (e.g. DOMPurify.sanitize).' },
|
|
92
|
-
{ text: 'Avoid rendering user-generated strings with dangerouslySetInnerHTML.' },
|
|
93
|
-
];
|
|
94
|
-
helpers.reportViolation({
|
|
95
|
-
description: messages.unsafeDangerouslySetInnerHTML(),
|
|
96
|
-
code: ATTRIBUTE_NAME,
|
|
97
|
-
suggestions: suggestions,
|
|
98
|
-
span: attribute.span,
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const missingAsyncCleanup: import("../types").Rule;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const noUnsafeDangerouslySetInnerHTML: import("../types").Rule;
|