@opensip-cli/checks-typescript 0.1.10 → 0.1.11
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/README.md +4 -2
- package/dist/__tests__/all-checks-execute.test.d.ts.map +1 -1
- package/dist/__tests__/all-checks-execute.test.js +0 -1
- package/dist/__tests__/all-checks-execute.test.js.map +1 -1
- package/dist/__tests__/behavior-fixtures-2.test.d.ts.map +1 -1
- package/dist/__tests__/behavior-fixtures-2.test.js +0 -1
- package/dist/__tests__/behavior-fixtures-2.test.js.map +1 -1
- package/dist/__tests__/behavior-fixtures-3.test.d.ts.map +1 -1
- package/dist/__tests__/behavior-fixtures-3.test.js +0 -1
- package/dist/__tests__/behavior-fixtures-3.test.js.map +1 -1
- package/dist/__tests__/behavior-fixtures-4.test.d.ts.map +1 -1
- package/dist/__tests__/behavior-fixtures-4.test.js +0 -1
- package/dist/__tests__/behavior-fixtures-4.test.js.map +1 -1
- package/dist/__tests__/behavior-fixtures-5.test.d.ts.map +1 -1
- package/dist/__tests__/behavior-fixtures-5.test.js +0 -1
- package/dist/__tests__/behavior-fixtures-5.test.js.map +1 -1
- package/dist/__tests__/behavior-fixtures-6.test.js +10 -0
- package/dist/__tests__/behavior-fixtures-6.test.js.map +1 -1
- package/dist/__tests__/behavior-fixtures.test.d.ts.map +1 -1
- package/dist/__tests__/behavior-fixtures.test.js +2 -4
- package/dist/__tests__/behavior-fixtures.test.js.map +1 -1
- package/dist/__tests__/branch-fixtures-2.test.d.ts.map +1 -1
- package/dist/__tests__/branch-fixtures-2.test.js +0 -1
- package/dist/__tests__/branch-fixtures-2.test.js.map +1 -1
- package/dist/__tests__/branch-fixtures-3.test.d.ts.map +1 -1
- package/dist/__tests__/branch-fixtures-3.test.js +0 -1
- package/dist/__tests__/branch-fixtures-3.test.js.map +1 -1
- package/dist/__tests__/branch-fixtures.test.d.ts.map +1 -1
- package/dist/__tests__/branch-fixtures.test.js +0 -1
- package/dist/__tests__/branch-fixtures.test.js.map +1 -1
- package/dist/checks/architecture/__tests__/live-view-through-cli-live.test.d.ts +2 -0
- package/dist/checks/architecture/__tests__/live-view-through-cli-live.test.d.ts.map +1 -0
- package/dist/checks/architecture/__tests__/live-view-through-cli-live.test.js +13 -0
- package/dist/checks/architecture/__tests__/live-view-through-cli-live.test.js.map +1 -0
- package/dist/checks/architecture/contracts-schema-consistency.d.ts.map +1 -1
- package/dist/checks/architecture/contracts-schema-consistency.js +0 -3
- package/dist/checks/architecture/contracts-schema-consistency.js.map +1 -1
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.d.ts.map +1 -1
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.js +1 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.js.map +1 -1
- package/dist/checks/architecture/index.d.ts +1 -0
- package/dist/checks/architecture/index.d.ts.map +1 -1
- package/dist/checks/architecture/index.js +1 -0
- package/dist/checks/architecture/index.js.map +1 -1
- package/dist/checks/architecture/live-view-through-cli-live.d.ts +8 -0
- package/dist/checks/architecture/live-view-through-cli-live.d.ts.map +1 -0
- package/dist/checks/architecture/live-view-through-cli-live.js +43 -0
- package/dist/checks/architecture/live-view-through-cli-live.js.map +1 -0
- package/dist/checks/architecture/missing-type-exports.d.ts.map +1 -1
- package/dist/checks/architecture/missing-type-exports.js +1 -1
- package/dist/checks/architecture/missing-type-exports.js.map +1 -1
- package/dist/checks/architecture/module-coupling-fan-out.d.ts.map +1 -1
- package/dist/checks/architecture/module-coupling-fan-out.js +6 -2
- package/dist/checks/architecture/module-coupling-fan-out.js.map +1 -1
- package/dist/checks/architecture/no-bootstrap-tool-import.d.ts.map +1 -1
- package/dist/checks/architecture/no-bootstrap-tool-import.js +1 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.js.map +1 -1
- package/dist/checks/architecture/no-run-done-result.d.ts.map +1 -1
- package/dist/checks/architecture/no-run-done-result.js +1 -0
- package/dist/checks/architecture/no-run-done-result.js.map +1 -1
- package/dist/checks/architecture/package-json-exports-field.d.ts.map +1 -1
- package/dist/checks/architecture/package-json-exports-field.js +1 -1
- package/dist/checks/architecture/package-json-exports-field.js.map +1 -1
- package/dist/checks/architecture/phantom-dependency-detection.d.ts.map +1 -1
- package/dist/checks/architecture/phantom-dependency-detection.js +0 -3
- package/dist/checks/architecture/phantom-dependency-detection.js.map +1 -1
- package/dist/checks/architecture/tsconfig-extends-validation.d.ts.map +1 -1
- package/dist/checks/architecture/tsconfig-extends-validation.js +0 -2
- package/dist/checks/architecture/tsconfig-extends-validation.js.map +1 -1
- package/dist/checks/quality/code-structure/__tests__/duplicate-utility-lang-substrate.test.d.ts +5 -0
- package/dist/checks/quality/code-structure/__tests__/duplicate-utility-lang-substrate.test.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/__tests__/duplicate-utility-lang-substrate.test.js +17 -0
- package/dist/checks/quality/code-structure/__tests__/duplicate-utility-lang-substrate.test.js.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions-config.d.ts +18 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions-config.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions-config.js +36 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions-config.js.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions-helpers.d.ts +15 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions-helpers.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions-helpers.js +288 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions-helpers.js.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts +1 -26
- package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts.map +1 -1
- package/dist/checks/quality/code-structure/duplicate-utility-functions.js +3 -407
- package/dist/checks/quality/code-structure/duplicate-utility-functions.js.map +1 -1
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js +39 -2
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js.map +1 -1
- package/dist/checks/quality/data-integrity/array-validation-detectors.d.ts +17 -0
- package/dist/checks/quality/data-integrity/array-validation-detectors.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/array-validation-detectors.js +184 -0
- package/dist/checks/quality/data-integrity/array-validation-detectors.js.map +1 -0
- package/dist/checks/quality/data-integrity/array-validation.d.ts +0 -2
- package/dist/checks/quality/data-integrity/array-validation.d.ts.map +1 -1
- package/dist/checks/quality/data-integrity/array-validation.js +2 -360
- package/dist/checks/quality/data-integrity/array-validation.js.map +1 -1
- package/dist/checks/quality/data-integrity/database-schema-validation.d.ts.map +1 -1
- package/dist/checks/quality/data-integrity/database-schema-validation.js +0 -1
- package/dist/checks/quality/data-integrity/database-schema-validation.js.map +1 -1
- package/dist/checks/quality/data-integrity/null-safety-analyze.d.ts +33 -0
- package/dist/checks/quality/data-integrity/null-safety-analyze.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety-analyze.js +164 -0
- package/dist/checks/quality/data-integrity/null-safety-analyze.js.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety-config.d.ts +50 -0
- package/dist/checks/quality/data-integrity/null-safety-config.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety-config.js +69 -0
- package/dist/checks/quality/data-integrity/null-safety-config.js.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety-heuristics.d.ts +76 -0
- package/dist/checks/quality/data-integrity/null-safety-heuristics.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety-heuristics.js +276 -0
- package/dist/checks/quality/data-integrity/null-safety-heuristics.js.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety-prefixes.d.ts +13 -0
- package/dist/checks/quality/data-integrity/null-safety-prefixes.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety-prefixes.js +333 -0
- package/dist/checks/quality/data-integrity/null-safety-prefixes.js.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety.d.ts +2 -82
- package/dist/checks/quality/data-integrity/null-safety.d.ts.map +1 -1
- package/dist/checks/quality/data-integrity/null-safety.js +3 -796
- package/dist/checks/quality/data-integrity/null-safety.js.map +1 -1
- package/dist/checks/quality/frontend/test-only-frontend-modules.d.ts.map +1 -1
- package/dist/checks/quality/frontend/test-only-frontend-modules.js +0 -2
- package/dist/checks/quality/frontend/test-only-frontend-modules.js.map +1 -1
- package/dist/checks/quality/linting/typescript-frontend.d.ts.map +1 -1
- package/dist/checks/quality/linting/typescript-frontend.js +1 -0
- package/dist/checks/quality/linting/typescript-frontend.js.map +1 -1
- package/dist/checks/quality/observability/logger-event-name-format.d.ts.map +1 -1
- package/dist/checks/quality/observability/logger-event-name-format.js +0 -1
- package/dist/checks/quality/observability/logger-event-name-format.js.map +1 -1
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.d.ts.map +1 -1
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.js +2 -3
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.js.map +1 -1
- package/dist/checks/quality/patterns/__tests__/async-waterfall-sequential.test.d.ts +8 -0
- package/dist/checks/quality/patterns/__tests__/async-waterfall-sequential.test.d.ts.map +1 -0
- package/dist/checks/quality/patterns/__tests__/async-waterfall-sequential.test.js +87 -0
- package/dist/checks/quality/patterns/__tests__/async-waterfall-sequential.test.js.map +1 -0
- package/dist/checks/quality/patterns/__tests__/error-handling-probes.test.d.ts +2 -0
- package/dist/checks/quality/patterns/__tests__/error-handling-probes.test.d.ts.map +1 -0
- package/dist/checks/quality/patterns/__tests__/error-handling-probes.test.js +51 -0
- package/dist/checks/quality/patterns/__tests__/error-handling-probes.test.js.map +1 -0
- package/dist/checks/quality/patterns/__tests__/result-pattern-registration-guards.test.d.ts +2 -0
- package/dist/checks/quality/patterns/__tests__/result-pattern-registration-guards.test.d.ts.map +1 -0
- package/dist/checks/quality/patterns/__tests__/result-pattern-registration-guards.test.js +89 -0
- package/dist/checks/quality/patterns/__tests__/result-pattern-registration-guards.test.js.map +1 -0
- package/dist/checks/quality/patterns/__tests__/throws-documentation-analyze.test.d.ts +5 -0
- package/dist/checks/quality/patterns/__tests__/throws-documentation-analyze.test.d.ts.map +1 -0
- package/dist/checks/quality/patterns/__tests__/throws-documentation-analyze.test.js +78 -0
- package/dist/checks/quality/patterns/__tests__/throws-documentation-analyze.test.js.map +1 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js +44 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js.map +1 -1
- package/dist/checks/quality/patterns/async-waterfall-analysis.d.ts +17 -0
- package/dist/checks/quality/patterns/async-waterfall-analysis.d.ts.map +1 -0
- package/dist/checks/quality/patterns/async-waterfall-analysis.js +215 -0
- package/dist/checks/quality/patterns/async-waterfall-analysis.js.map +1 -0
- package/dist/checks/quality/patterns/async-waterfall-branch-keys.d.ts +6 -0
- package/dist/checks/quality/patterns/async-waterfall-branch-keys.d.ts.map +1 -0
- package/dist/checks/quality/patterns/async-waterfall-branch-keys.js +54 -0
- package/dist/checks/quality/patterns/async-waterfall-branch-keys.js.map +1 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.d.ts.map +1 -1
- package/dist/checks/quality/patterns/async-waterfall-detection.js +3 -352
- package/dist/checks/quality/patterns/async-waterfall-detection.js.map +1 -1
- package/dist/checks/quality/patterns/containing-function-name.d.ts +3 -0
- package/dist/checks/quality/patterns/containing-function-name.d.ts.map +1 -0
- package/dist/checks/quality/patterns/containing-function-name.js +21 -0
- package/dist/checks/quality/patterns/containing-function-name.js.map +1 -0
- package/dist/checks/quality/patterns/error-handling-quality.d.ts +3 -0
- package/dist/checks/quality/patterns/error-handling-quality.d.ts.map +1 -1
- package/dist/checks/quality/patterns/error-handling-quality.js +150 -30
- package/dist/checks/quality/patterns/error-handling-quality.js.map +1 -1
- package/dist/checks/quality/patterns/result-pattern-consistency.d.ts +3 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.d.ts.map +1 -1
- package/dist/checks/quality/patterns/result-pattern-consistency.js +136 -69
- package/dist/checks/quality/patterns/result-pattern-consistency.js.map +1 -1
- package/dist/checks/quality/patterns/throws-documentation-analyze.d.ts +14 -0
- package/dist/checks/quality/patterns/throws-documentation-analyze.d.ts.map +1 -0
- package/dist/checks/quality/patterns/throws-documentation-analyze.js +352 -0
- package/dist/checks/quality/patterns/throws-documentation-analyze.js.map +1 -0
- package/dist/checks/quality/patterns/throws-documentation-constants.d.ts +15 -0
- package/dist/checks/quality/patterns/throws-documentation-constants.d.ts.map +1 -0
- package/dist/checks/quality/patterns/throws-documentation-constants.js +94 -0
- package/dist/checks/quality/patterns/throws-documentation-constants.js.map +1 -0
- package/dist/checks/quality/patterns/throws-documentation.d.ts +1 -11
- package/dist/checks/quality/patterns/throws-documentation.d.ts.map +1 -1
- package/dist/checks/quality/patterns/throws-documentation.js +4 -472
- package/dist/checks/quality/patterns/throws-documentation.js.map +1 -1
- package/dist/checks/quality/patterns/toctou-race-condition-classify.d.ts +23 -0
- package/dist/checks/quality/patterns/toctou-race-condition-classify.d.ts.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition-classify.js +125 -0
- package/dist/checks/quality/patterns/toctou-race-condition-classify.js.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition-collection.d.ts +24 -0
- package/dist/checks/quality/patterns/toctou-race-condition-collection.d.ts.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition-collection.js +248 -0
- package/dist/checks/quality/patterns/toctou-race-condition-collection.js.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition-constants.d.ts +32 -0
- package/dist/checks/quality/patterns/toctou-race-condition-constants.d.ts.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition-constants.js +115 -0
- package/dist/checks/quality/patterns/toctou-race-condition-constants.js.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition.d.ts +1 -29
- package/dist/checks/quality/patterns/toctou-race-condition.d.ts.map +1 -1
- package/dist/checks/quality/patterns/toctou-race-condition.js +11 -536
- package/dist/checks/quality/patterns/toctou-race-condition.js.map +1 -1
- package/dist/checks/quality/unused-config-options.d.ts.map +1 -1
- package/dist/checks/quality/unused-config-options.js +0 -4
- package/dist/checks/quality/unused-config-options.js.map +1 -1
- package/dist/checks/resilience/__tests__/detached-promises-sync-detection.test.d.ts +2 -0
- package/dist/checks/resilience/__tests__/detached-promises-sync-detection.test.d.ts.map +1 -0
- package/dist/checks/resilience/__tests__/detached-promises-sync-detection.test.js +98 -0
- package/dist/checks/resilience/__tests__/detached-promises-sync-detection.test.js.map +1 -0
- package/dist/checks/resilience/callback-invocation-safe.d.ts.map +1 -1
- package/dist/checks/resilience/callback-invocation-safe.js +0 -1
- package/dist/checks/resilience/callback-invocation-safe.js.map +1 -1
- package/dist/checks/resilience/context-leakage.d.ts.map +1 -1
- package/dist/checks/resilience/context-leakage.js +1 -0
- package/dist/checks/resilience/context-leakage.js.map +1 -1
- package/dist/checks/resilience/detached-promises-detection.d.ts +7 -0
- package/dist/checks/resilience/detached-promises-detection.d.ts.map +1 -0
- package/dist/checks/resilience/detached-promises-detection.js +228 -0
- package/dist/checks/resilience/detached-promises-detection.js.map +1 -0
- package/dist/checks/resilience/detached-promises-sync-constants.d.ts +36 -0
- package/dist/checks/resilience/detached-promises-sync-constants.d.ts.map +1 -0
- package/dist/checks/resilience/detached-promises-sync-constants.js +299 -0
- package/dist/checks/resilience/detached-promises-sync-constants.js.map +1 -0
- package/dist/checks/resilience/detached-promises-sync-detection.d.ts +14 -0
- package/dist/checks/resilience/detached-promises-sync-detection.d.ts.map +1 -0
- package/dist/checks/resilience/detached-promises-sync-detection.js +69 -0
- package/dist/checks/resilience/detached-promises-sync-detection.js.map +1 -0
- package/dist/checks/resilience/detached-promises.d.ts +1 -14
- package/dist/checks/resilience/detached-promises.d.ts.map +1 -1
- package/dist/checks/resilience/detached-promises.js +2 -598
- package/dist/checks/resilience/detached-promises.js.map +1 -1
- package/dist/checks/resilience/no-raw-fetch.d.ts.map +1 -1
- package/dist/checks/resilience/no-raw-fetch.js +1 -0
- package/dist/checks/resilience/no-raw-fetch.js.map +1 -1
- package/dist/checks/resilience/no-unbounded-concurrency.d.ts.map +1 -1
- package/dist/checks/resilience/no-unbounded-concurrency.js +1 -0
- package/dist/checks/resilience/no-unbounded-concurrency.js.map +1 -1
- package/dist/checks/security/sql-injection.d.ts.map +1 -1
- package/dist/checks/security/sql-injection.js +0 -1
- package/dist/checks/security/sql-injection.js.map +1 -1
- package/dist/display/architecture.d.ts.map +1 -1
- package/dist/display/architecture.js +1 -0
- package/dist/display/architecture.js.map +1 -1
- package/dist/display/types.d.ts.map +1 -1
- package/dist/display/types.js +0 -1
- package/dist/display/types.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
export const QUICK_FILTER_KEYWORDS = [
|
|
3
|
+
'[]',
|
|
4
|
+
'Array',
|
|
5
|
+
'array',
|
|
6
|
+
'.length',
|
|
7
|
+
'.push',
|
|
8
|
+
'.pop',
|
|
9
|
+
'.map(',
|
|
10
|
+
'.filter(',
|
|
11
|
+
];
|
|
12
|
+
const RELAXED_VALIDATION_PATHS = [
|
|
13
|
+
/\/internal\//,
|
|
14
|
+
/\/utils\//,
|
|
15
|
+
/\/helpers\//,
|
|
16
|
+
/\/cli\//,
|
|
17
|
+
/\/scripts\//,
|
|
18
|
+
];
|
|
19
|
+
const COMPLEX_TYPE_PATTERNS = ['Record<', 'Map<', '=> ', ': (', 'Promise<', 'Observable<'];
|
|
20
|
+
export function isTopLevelArrayType(typeNode) {
|
|
21
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (ts.isTypeReferenceNode(typeNode)) {
|
|
25
|
+
const typeName = typeNode.typeName;
|
|
26
|
+
if (ts.isIdentifier(typeName)) {
|
|
27
|
+
const name = typeName.text;
|
|
28
|
+
if (name === 'Array' || name === 'ReadonlyArray') {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (ts.isUnionTypeNode(typeNode)) {
|
|
34
|
+
return typeNode.types.length > 0 && typeNode.types.every((t) => isTopLevelArrayType(t));
|
|
35
|
+
}
|
|
36
|
+
if (ts.isParenthesizedTypeNode(typeNode)) {
|
|
37
|
+
return isTopLevelArrayType(typeNode.type);
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
export function isRelaxedValidationPath(filePath) {
|
|
42
|
+
return RELAXED_VALIDATION_PATHS.some((pattern) => pattern.test(filePath));
|
|
43
|
+
}
|
|
44
|
+
export function isComplexNestedType(typeText) {
|
|
45
|
+
return COMPLEX_TYPE_PATTERNS.some((pattern) => typeText.includes(pattern));
|
|
46
|
+
}
|
|
47
|
+
export function isLengthAccess(node, paramName, sourceFile) {
|
|
48
|
+
if (!ts.isPropertyAccessExpression(node))
|
|
49
|
+
return false;
|
|
50
|
+
const objName = node.expression.getText(sourceFile);
|
|
51
|
+
const propName = node.name.getText(sourceFile);
|
|
52
|
+
return objName === paramName && propName === 'length';
|
|
53
|
+
}
|
|
54
|
+
export function isArrayIsArrayCall(node, paramName, sourceFile) {
|
|
55
|
+
if (!ts.isCallExpression(node))
|
|
56
|
+
return false;
|
|
57
|
+
const callText = node.expression.getText(sourceFile);
|
|
58
|
+
if (callText !== 'Array.isArray')
|
|
59
|
+
return false;
|
|
60
|
+
const arg = node.arguments[0]?.getText(sourceFile);
|
|
61
|
+
return arg === paramName;
|
|
62
|
+
}
|
|
63
|
+
export function isZodValidationCall(node, sourceFile) {
|
|
64
|
+
if (!ts.isCallExpression(node))
|
|
65
|
+
return false;
|
|
66
|
+
if (!ts.isPropertyAccessExpression(node.expression))
|
|
67
|
+
return false;
|
|
68
|
+
const methodName = node.expression.name.getText(sourceFile);
|
|
69
|
+
return methodName === 'parse' || methodName === 'safeParse';
|
|
70
|
+
}
|
|
71
|
+
export function isValidationFunctionCall(node) {
|
|
72
|
+
if (!ts.isCallExpression(node))
|
|
73
|
+
return false;
|
|
74
|
+
if (!ts.isIdentifier(node.expression))
|
|
75
|
+
return false;
|
|
76
|
+
const funcName = node.expression.text.toLowerCase();
|
|
77
|
+
return funcName.includes('validate') || funcName.includes('check');
|
|
78
|
+
}
|
|
79
|
+
export function isIterationOverParam(node, paramName, sourceFile) {
|
|
80
|
+
if (ts.isForOfStatement(node)) {
|
|
81
|
+
const iterableText = node.expression.getText(sourceFile);
|
|
82
|
+
if (iterableText === paramName || iterableText.startsWith(`${paramName}.`)) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
87
|
+
const objText = node.expression.expression.getText(sourceFile);
|
|
88
|
+
const methodName = node.expression.name.getText(sourceFile);
|
|
89
|
+
const iterationMethods = [
|
|
90
|
+
'forEach',
|
|
91
|
+
'map',
|
|
92
|
+
'filter',
|
|
93
|
+
'some',
|
|
94
|
+
'every',
|
|
95
|
+
'find',
|
|
96
|
+
'findIndex',
|
|
97
|
+
'findLast',
|
|
98
|
+
'findLastIndex',
|
|
99
|
+
'reduce',
|
|
100
|
+
'reduceRight',
|
|
101
|
+
'flatMap',
|
|
102
|
+
'flat',
|
|
103
|
+
'slice',
|
|
104
|
+
'includes',
|
|
105
|
+
'indexOf',
|
|
106
|
+
'lastIndexOf',
|
|
107
|
+
'join',
|
|
108
|
+
'concat',
|
|
109
|
+
'entries',
|
|
110
|
+
'values',
|
|
111
|
+
'keys',
|
|
112
|
+
'at',
|
|
113
|
+
'toSorted',
|
|
114
|
+
'toReversed',
|
|
115
|
+
'toSpliced',
|
|
116
|
+
'with',
|
|
117
|
+
];
|
|
118
|
+
if (objText === paramName && iterationMethods.includes(methodName)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
export function isOutSinkUsage(node, paramName, sourceFile) {
|
|
125
|
+
if (!ts.isCallExpression(node))
|
|
126
|
+
return false;
|
|
127
|
+
if (!ts.isPropertyAccessExpression(node.expression))
|
|
128
|
+
return false;
|
|
129
|
+
const objText = node.expression.expression.getText(sourceFile);
|
|
130
|
+
if (objText !== paramName)
|
|
131
|
+
return false;
|
|
132
|
+
const methodName = node.expression.name.getText(sourceFile);
|
|
133
|
+
return methodName === 'push' || methodName === 'unshift' || methodName === 'splice';
|
|
134
|
+
}
|
|
135
|
+
export function isIndexedAccess(node, paramName, sourceFile) {
|
|
136
|
+
if (!ts.isElementAccessExpression(node))
|
|
137
|
+
return false;
|
|
138
|
+
return node.expression.getText(sourceFile) === paramName;
|
|
139
|
+
}
|
|
140
|
+
export function isSpreadOfParam(node, paramName, sourceFile) {
|
|
141
|
+
if (ts.isSpreadElement(node) || ts.isSpreadAssignment(node)) {
|
|
142
|
+
return node.expression.getText(sourceFile) === paramName;
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
export function isForwardedToCall(node, paramName, _sourceFile) {
|
|
147
|
+
if (!ts.isCallExpression(node) && !ts.isNewExpression(node))
|
|
148
|
+
return false;
|
|
149
|
+
const args = node.arguments;
|
|
150
|
+
if (!args)
|
|
151
|
+
return false;
|
|
152
|
+
for (const arg of args) {
|
|
153
|
+
if (ts.isIdentifier(arg) && arg.text === paramName) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
if (ts.isAsExpression(arg) || ts.isSatisfiesExpression(arg)) {
|
|
157
|
+
const inner = arg.expression;
|
|
158
|
+
if (ts.isIdentifier(inner) && inner.text === paramName) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
export function isShorthandPropertyReference(node, paramName) {
|
|
166
|
+
if (!ts.isShorthandPropertyAssignment(node))
|
|
167
|
+
return false;
|
|
168
|
+
return node.name.text === paramName;
|
|
169
|
+
}
|
|
170
|
+
export function isOptionalHandling(node, paramName, sourceFile) {
|
|
171
|
+
const nodeText = node.getText(sourceFile);
|
|
172
|
+
if (nodeText.includes(`${paramName}?.`)) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (ts.isBinaryExpression(node) &&
|
|
176
|
+
node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
177
|
+
const leftText = node.left.getText(sourceFile);
|
|
178
|
+
if (leftText === paramName) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=array-validation-detectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"array-validation-detectors.js","sourceRoot":"","sources":["../../../../src/checks/quality/data-integrity/array-validation-detectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Detects array parameters without proper validation.
|
|
5
5
|
* Ensures arrays are validated for length, type, and content before processing.
|
|
6
|
-
*
|
|
7
6
|
*/
|
|
8
7
|
/**
|
|
9
8
|
* Check: quality/array-validation
|
|
10
9
|
*
|
|
11
10
|
* Detects array parameters without proper validation to prevent
|
|
12
11
|
* runtime errors from unvalidated array inputs.
|
|
13
|
-
*
|
|
14
12
|
*/
|
|
15
13
|
export declare const arrayValidation: import("@opensip-cli/fitness").Check;
|
|
16
14
|
//# sourceMappingURL=array-validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array-validation.d.ts","sourceRoot":"","sources":["../../../../src/checks/quality/data-integrity/array-validation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"array-validation.d.ts","sourceRoot":"","sources":["../../../../src/checks/quality/data-integrity/array-validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA8JH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,sCA2B1B,CAAC"}
|
|
@@ -1,340 +1,19 @@
|
|
|
1
|
-
// @fitness-ignore-file file-length-limit -- cohesive single-check module; splitting risks breaking the detector contract
|
|
2
1
|
/**
|
|
3
2
|
* @fileoverview Array Parameter Validation Check
|
|
4
3
|
*
|
|
5
4
|
* Detects array parameters without proper validation.
|
|
6
5
|
* Ensures arrays are validated for length, type, and content before processing.
|
|
7
|
-
*
|
|
8
6
|
*/
|
|
9
7
|
import { defineCheck, isTestFile } from '@opensip-cli/fitness';
|
|
10
8
|
import { getSharedSourceFile } from '@opensip-cli/lang-typescript';
|
|
11
9
|
import * as ts from 'typescript';
|
|
12
|
-
|
|
13
|
-
* Quick filter keywords for array validation patterns
|
|
14
|
-
*/
|
|
15
|
-
const QUICK_FILTER_KEYWORDS = [
|
|
16
|
-
'[]',
|
|
17
|
-
'Array',
|
|
18
|
-
'array',
|
|
19
|
-
'.length',
|
|
20
|
-
'.push',
|
|
21
|
-
'.pop',
|
|
22
|
-
'.map(',
|
|
23
|
-
'.filter(',
|
|
24
|
-
];
|
|
25
|
-
/**
|
|
26
|
-
* Paths where array validation requirements are relaxed
|
|
27
|
-
*/
|
|
28
|
-
const RELAXED_VALIDATION_PATHS = [
|
|
29
|
-
/\/internal\//,
|
|
30
|
-
/\/utils\//,
|
|
31
|
-
/\/helpers\//,
|
|
32
|
-
/\/cli\//,
|
|
33
|
-
/\/scripts\//,
|
|
34
|
-
];
|
|
35
|
-
/**
|
|
36
|
-
* Type patterns that indicate complex/nested types where validation is harder to detect
|
|
37
|
-
*/
|
|
38
|
-
const COMPLEX_TYPE_PATTERNS = [
|
|
39
|
-
'Record<',
|
|
40
|
-
'Map<',
|
|
41
|
-
'=> ', // Function type
|
|
42
|
-
': (', // Function type with parens
|
|
43
|
-
'Promise<', // Async wrappers
|
|
44
|
-
'Observable<',
|
|
45
|
-
];
|
|
46
|
-
/**
|
|
47
|
-
* Check whether a parameter's type is a top-level array type (e.g. `string[]`, `Array<T>`)
|
|
48
|
-
* as opposed to an object/interface/intersection type that merely _contains_ array-typed
|
|
49
|
-
* properties (e.g. `{ items: string[] }`, `Foo & { tags: string[] }`).
|
|
50
|
-
*
|
|
51
|
-
* This uses the TypeScript AST node kind rather than text matching, eliminating false
|
|
52
|
-
* positives for object parameters whose nested properties happen to be arrays.
|
|
53
|
-
*/
|
|
54
|
-
function isTopLevelArrayType(typeNode) {
|
|
55
|
-
// Direct array type: `string[]`, `Foo[]`
|
|
56
|
-
if (ts.isArrayTypeNode(typeNode)) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
// Generic Array reference: `Array<string>`, `ReadonlyArray<Foo>`
|
|
60
|
-
if (ts.isTypeReferenceNode(typeNode)) {
|
|
61
|
-
const typeName = typeNode.typeName;
|
|
62
|
-
if (ts.isIdentifier(typeName)) {
|
|
63
|
-
const name = typeName.text;
|
|
64
|
-
if (name === 'Array' || name === 'ReadonlyArray') {
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Union type: check if ALL branches are arrays (e.g. `string[] | number[]`)
|
|
70
|
-
if (ts.isUnionTypeNode(typeNode)) {
|
|
71
|
-
return typeNode.types.length > 0 && typeNode.types.every((t) => isTopLevelArrayType(t));
|
|
72
|
-
}
|
|
73
|
-
// Parenthesized type: unwrap `(string[])`
|
|
74
|
-
if (ts.isParenthesizedTypeNode(typeNode)) {
|
|
75
|
-
return isTopLevelArrayType(typeNode.type);
|
|
76
|
-
}
|
|
77
|
-
// Everything else (object literals, intersection types, type references to interfaces,
|
|
78
|
-
// mapped types, conditional types, etc.) is NOT a top-level array even if the type
|
|
79
|
-
// text contains `[]` in nested positions.
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Check if a file path is in a relaxed validation context
|
|
84
|
-
*/
|
|
85
|
-
function isRelaxedValidationPath(filePath) {
|
|
86
|
-
return RELAXED_VALIDATION_PATHS.some((pattern) => pattern.test(filePath));
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Check if a type is a complex nested type where validation detection is unreliable
|
|
90
|
-
*/
|
|
91
|
-
function isComplexNestedType(typeText) {
|
|
92
|
-
return COMPLEX_TYPE_PATTERNS.some((pattern) => typeText.includes(pattern));
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Check if node is a .length access on the parameter
|
|
96
|
-
* @param node - The TypeScript AST node to check
|
|
97
|
-
* @param paramName - The name of the parameter to check for
|
|
98
|
-
* @param sourceFile - The TypeScript source file for text extraction
|
|
99
|
-
* @returns True if the node is a .length access on the specified parameter
|
|
100
|
-
*/
|
|
101
|
-
function isLengthAccess(node, paramName, sourceFile) {
|
|
102
|
-
if (!ts.isPropertyAccessExpression(node))
|
|
103
|
-
return false;
|
|
104
|
-
const objName = node.expression.getText(sourceFile);
|
|
105
|
-
const propName = node.name.getText(sourceFile);
|
|
106
|
-
return objName === paramName && propName === 'length';
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Check if node is an Array.isArray() call on the parameter
|
|
110
|
-
* @param node - The TypeScript AST node to check
|
|
111
|
-
* @param paramName - The name of the parameter to check for
|
|
112
|
-
* @param sourceFile - The TypeScript source file for text extraction
|
|
113
|
-
* @returns True if the node is an Array.isArray() call on the specified parameter
|
|
114
|
-
*/
|
|
115
|
-
function isArrayIsArrayCall(node, paramName, sourceFile) {
|
|
116
|
-
if (!ts.isCallExpression(node))
|
|
117
|
-
return false;
|
|
118
|
-
const callText = node.expression.getText(sourceFile);
|
|
119
|
-
if (callText !== 'Array.isArray')
|
|
120
|
-
return false;
|
|
121
|
-
const arg = node.arguments[0]?.getText(sourceFile);
|
|
122
|
-
return arg === paramName;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Check if node is a Zod schema validation call
|
|
126
|
-
* @param node - The TypeScript AST node to check
|
|
127
|
-
* @param sourceFile - The TypeScript source file for text extraction
|
|
128
|
-
* @returns True if the node is a Zod .parse() or .safeParse() call
|
|
129
|
-
*/
|
|
130
|
-
function isZodValidationCall(node, sourceFile) {
|
|
131
|
-
if (!ts.isCallExpression(node))
|
|
132
|
-
return false;
|
|
133
|
-
if (!ts.isPropertyAccessExpression(node.expression))
|
|
134
|
-
return false;
|
|
135
|
-
const methodName = node.expression.name.getText(sourceFile);
|
|
136
|
-
return methodName === 'parse' || methodName === 'safeParse';
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Check if node is a validation function call
|
|
140
|
-
* @param node - The TypeScript AST node to check
|
|
141
|
-
* @returns True if the node is a call to a function with 'validate' or 'check' in its name
|
|
142
|
-
*/
|
|
143
|
-
function isValidationFunctionCall(node) {
|
|
144
|
-
if (!ts.isCallExpression(node))
|
|
145
|
-
return false;
|
|
146
|
-
if (!ts.isIdentifier(node.expression))
|
|
147
|
-
return false;
|
|
148
|
-
const funcName = node.expression.text.toLowerCase();
|
|
149
|
-
return funcName.includes('validate') || funcName.includes('check');
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Check if node is an iteration over the parameter (for...of, forEach, map, filter,
|
|
153
|
-
* slice, includes, join, indexOf, concat, flat, etc.)
|
|
154
|
-
*
|
|
155
|
-
* Iteration implies validation because it handles empty arrays gracefully and
|
|
156
|
-
* cannot blow up the runtime — the worst case is no work done.
|
|
157
|
-
*/
|
|
158
|
-
function isIterationOverParam(node, paramName, sourceFile) {
|
|
159
|
-
// for...of statement
|
|
160
|
-
if (ts.isForOfStatement(node)) {
|
|
161
|
-
const iterableText = node.expression.getText(sourceFile);
|
|
162
|
-
if (iterableText === paramName || iterableText.startsWith(`${paramName}.`)) {
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Array prototype methods that internally iterate (and therefore handle empty
|
|
167
|
-
// arrays gracefully) or otherwise produce a bounded view. These are
|
|
168
|
-
// validation-equivalent because they don't crash on empty/short input.
|
|
169
|
-
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
170
|
-
const objText = node.expression.expression.getText(sourceFile);
|
|
171
|
-
const methodName = node.expression.name.getText(sourceFile);
|
|
172
|
-
const iterationMethods = [
|
|
173
|
-
'forEach',
|
|
174
|
-
'map',
|
|
175
|
-
'filter',
|
|
176
|
-
'some',
|
|
177
|
-
'every',
|
|
178
|
-
'find',
|
|
179
|
-
'findIndex',
|
|
180
|
-
'findLast',
|
|
181
|
-
'findLastIndex',
|
|
182
|
-
'reduce',
|
|
183
|
-
'reduceRight',
|
|
184
|
-
'flatMap',
|
|
185
|
-
'flat',
|
|
186
|
-
'slice',
|
|
187
|
-
'includes',
|
|
188
|
-
'indexOf',
|
|
189
|
-
'lastIndexOf',
|
|
190
|
-
'join',
|
|
191
|
-
'concat',
|
|
192
|
-
'entries',
|
|
193
|
-
'values',
|
|
194
|
-
'keys',
|
|
195
|
-
'at',
|
|
196
|
-
'toSorted',
|
|
197
|
-
'toReversed',
|
|
198
|
-
'toSpliced',
|
|
199
|
-
'with',
|
|
200
|
-
];
|
|
201
|
-
if (objText === paramName && iterationMethods.includes(methodName)) {
|
|
202
|
-
return true;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Check if node is a write-only sink usage of the parameter.
|
|
209
|
-
*
|
|
210
|
-
* Out-array sinks (`bucket.push(...)`, `bucket.unshift(...)`) flag the param
|
|
211
|
-
* as a producer target — the function isn't *consuming* the array, it's
|
|
212
|
-
* writing into it. Validation belongs at the consumer, not the producer.
|
|
213
|
-
*/
|
|
214
|
-
function isOutSinkUsage(node, paramName, sourceFile) {
|
|
215
|
-
if (!ts.isCallExpression(node))
|
|
216
|
-
return false;
|
|
217
|
-
if (!ts.isPropertyAccessExpression(node.expression))
|
|
218
|
-
return false;
|
|
219
|
-
const objText = node.expression.expression.getText(sourceFile);
|
|
220
|
-
if (objText !== paramName)
|
|
221
|
-
return false;
|
|
222
|
-
const methodName = node.expression.name.getText(sourceFile);
|
|
223
|
-
// Mutating sink methods — caller is producing into the array.
|
|
224
|
-
return methodName === 'push' || methodName === 'unshift' || methodName === 'splice';
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Check if node is indexed access on the parameter (`param[i]`).
|
|
228
|
-
*
|
|
229
|
-
* Indexed access on a typed array yields `T | undefined` under
|
|
230
|
-
* `noUncheckedIndexedAccess`; under any reasonable consumer it implies
|
|
231
|
-
* defensive use and cannot crash on empty/short input the way an
|
|
232
|
-
* unconditional `.length`-less iteration would. Practically: if the body
|
|
233
|
-
* does `param[i]`, the author has already structured the access around
|
|
234
|
-
* length (or expects undefined).
|
|
235
|
-
*/
|
|
236
|
-
function isIndexedAccess(node, paramName, sourceFile) {
|
|
237
|
-
if (!ts.isElementAccessExpression(node))
|
|
238
|
-
return false;
|
|
239
|
-
return node.expression.getText(sourceFile) === paramName;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Check if node is a spread of the parameter (`...param`) inside a call
|
|
243
|
-
* argument list, array literal, or object literal.
|
|
244
|
-
*
|
|
245
|
-
* Spread iterates the array — equivalent to `for...of`.
|
|
246
|
-
*/
|
|
247
|
-
function isSpreadOfParam(node, paramName, sourceFile) {
|
|
248
|
-
if (ts.isSpreadElement(node) || ts.isSpreadAssignment(node)) {
|
|
249
|
-
return node.expression.getText(sourceFile) === paramName;
|
|
250
|
-
}
|
|
251
|
-
return false;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Check if node passes the parameter through to another function call.
|
|
255
|
-
*
|
|
256
|
-
* A pure pass-through forwarder (`return otherFn(..., param, ...)`) defers
|
|
257
|
-
* validation to the destination function — flagging the forwarder is a false
|
|
258
|
-
* positive, since the wrapper has nothing to validate against. The destination
|
|
259
|
-
* function (which actually consumes the array) is the meaningful validation
|
|
260
|
-
* site.
|
|
261
|
-
*
|
|
262
|
-
* Restricted to call-argument position (not the callee position) so we don't
|
|
263
|
-
* match the param being treated as a function.
|
|
264
|
-
*/
|
|
265
|
-
function isForwardedToCall(node, paramName, _sourceFile) {
|
|
266
|
-
if (!ts.isCallExpression(node) && !ts.isNewExpression(node))
|
|
267
|
-
return false;
|
|
268
|
-
const args = node.arguments;
|
|
269
|
-
/* v8 ignore next -- defensive AST/type guard */
|
|
270
|
-
if (!args)
|
|
271
|
-
return false;
|
|
272
|
-
for (const arg of args) {
|
|
273
|
-
if (ts.isIdentifier(arg) && arg.text === paramName) {
|
|
274
|
-
return true;
|
|
275
|
-
}
|
|
276
|
-
// `Array.from(param)`, `[...param]`, etc. handled by isSpreadOfParam.
|
|
277
|
-
// Type-cast forwards: `someFn(param as Foo)` or `someFn(param satisfies Foo)`.
|
|
278
|
-
if (ts.isAsExpression(arg) || ts.isSatisfiesExpression(arg)) {
|
|
279
|
-
const inner = arg.expression;
|
|
280
|
-
if (ts.isIdentifier(inner) && inner.text === paramName) {
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Check if node is a property-shorthand reference to the parameter inside
|
|
289
|
-
* an object literal (`return { param }`).
|
|
290
|
-
*
|
|
291
|
-
* The param value is being copied by reference into a result object — no
|
|
292
|
-
* iteration, no boundary crossing, the consumer of the returned object is
|
|
293
|
-
* the meaningful validation site (same logic as `isForwardedToCall`).
|
|
294
|
-
*/
|
|
295
|
-
function isShorthandPropertyReference(node, paramName) {
|
|
296
|
-
if (!ts.isShorthandPropertyAssignment(node))
|
|
297
|
-
return false;
|
|
298
|
-
return node.name.text === paramName;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Check if node is an optional chaining or nullish coalescing on the parameter
|
|
302
|
-
*/
|
|
303
|
-
function isOptionalHandling(node, paramName, sourceFile) {
|
|
304
|
-
const nodeText = node.getText(sourceFile);
|
|
305
|
-
// Optional chaining: param?.length, param?.map
|
|
306
|
-
if (nodeText.includes(`${paramName}?.`)) {
|
|
307
|
-
return true;
|
|
308
|
-
}
|
|
309
|
-
/* v8 ignore next -- defensive nullish fallback */
|
|
310
|
-
// Nullish coalescing: param ?? []
|
|
311
|
-
if (ts.isBinaryExpression(node) &&
|
|
312
|
-
node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
313
|
-
const leftText = node.left.getText(sourceFile);
|
|
314
|
-
if (leftText === paramName) {
|
|
315
|
-
return true;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return false;
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Check if function body contains array validation for the parameter
|
|
322
|
-
* @param node - The function declaration, method, or arrow function node
|
|
323
|
-
* @param param - The parameter declaration to check for validation
|
|
324
|
-
* @param sourceFile - The TypeScript source file for text extraction
|
|
325
|
-
* @returns True if the function body contains validation for the array parameter
|
|
326
|
-
*/
|
|
10
|
+
import { isArrayIsArrayCall, isComplexNestedType, isForwardedToCall, isIndexedAccess, isIterationOverParam, isLengthAccess, isOptionalHandling, isOutSinkUsage, isRelaxedValidationPath, isShorthandPropertyReference, isSpreadOfParam, isTopLevelArrayType, isValidationFunctionCall, isZodValidationCall, QUICK_FILTER_KEYWORDS, } from './array-validation-detectors.js';
|
|
327
11
|
function checkForArrayValidation(node, param, sourceFile) {
|
|
328
|
-
/* v8 ignore next -- defensive guard */
|
|
329
12
|
if (!node.body)
|
|
330
13
|
return false;
|
|
331
14
|
const paramName = ts.isIdentifier(param.name) ? param.name.text : null;
|
|
332
|
-
/* v8 ignore next -- defensive AST/type guard */
|
|
333
15
|
if (!paramName)
|
|
334
|
-
return true;
|
|
335
|
-
// Underscore-prefixed params are an established TS convention for
|
|
336
|
-
// "intentionally unused" — the body does not consume them, so demanding
|
|
337
|
-
// validation is meaningless.
|
|
16
|
+
return true;
|
|
338
17
|
if (paramName.startsWith('_'))
|
|
339
18
|
return true;
|
|
340
19
|
let hasValidation = false;
|
|
@@ -351,33 +30,24 @@ function checkForArrayValidation(node, param, sourceFile) {
|
|
|
351
30
|
if (isValidationFunctionCall(n)) {
|
|
352
31
|
hasValidation = true;
|
|
353
32
|
}
|
|
354
|
-
// Iteration patterns imply graceful handling of arrays
|
|
355
33
|
if (isIterationOverParam(n, paramName, sourceFile)) {
|
|
356
34
|
hasValidation = true;
|
|
357
35
|
}
|
|
358
|
-
// Optional chaining/nullish coalescing implies null safety
|
|
359
36
|
if (isOptionalHandling(n, paramName, sourceFile)) {
|
|
360
37
|
hasValidation = true;
|
|
361
38
|
}
|
|
362
|
-
// Out-array sinks (param.push(...)) — param is a producer target, not a
|
|
363
|
-
// consumer input. Validation belongs at the consumer.
|
|
364
39
|
if (isOutSinkUsage(n, paramName, sourceFile)) {
|
|
365
40
|
hasValidation = true;
|
|
366
41
|
}
|
|
367
|
-
// Indexed access (param[i]) — defensive, bounded by author intent.
|
|
368
42
|
if (isIndexedAccess(n, paramName, sourceFile)) {
|
|
369
43
|
hasValidation = true;
|
|
370
44
|
}
|
|
371
|
-
// Spread (...param) — iterates the array, equivalent to for...of.
|
|
372
45
|
if (isSpreadOfParam(n, paramName, sourceFile)) {
|
|
373
46
|
hasValidation = true;
|
|
374
47
|
}
|
|
375
|
-
// Forwarded to another call — destination owns validation.
|
|
376
48
|
if (isForwardedToCall(n, paramName, sourceFile)) {
|
|
377
49
|
hasValidation = true;
|
|
378
50
|
}
|
|
379
|
-
// Property-shorthand reference ({ param }) — pass-through into a result
|
|
380
|
-
// object; consumer of the result owns validation.
|
|
381
51
|
if (isShorthandPropertyReference(n, paramName)) {
|
|
382
52
|
hasValidation = true;
|
|
383
53
|
}
|
|
@@ -388,38 +58,22 @@ function checkForArrayValidation(node, param, sourceFile) {
|
|
|
388
58
|
visit(node.body);
|
|
389
59
|
return hasValidation;
|
|
390
60
|
}
|
|
391
|
-
/**
|
|
392
|
-
* Check function parameters for unvalidated arrays
|
|
393
|
-
* Note: Ignore directives are handled at the framework level in defineCheck()
|
|
394
|
-
* @param {CheckFunctionArrayParamsOptions} options - The check options
|
|
395
|
-
* @returns Array of violations
|
|
396
|
-
*/
|
|
397
61
|
function checkFunctionArrayParams(options) {
|
|
398
62
|
const { node, sourceFile, absolutePath } = options;
|
|
399
63
|
const violations = [];
|
|
400
|
-
// Skip files in relaxed validation paths
|
|
401
64
|
if (isRelaxedValidationPath(absolutePath)) {
|
|
402
65
|
return violations;
|
|
403
66
|
}
|
|
404
|
-
// Skip abstract methods (can't have validation in body)
|
|
405
67
|
if (ts.isMethodDeclaration(node) &&
|
|
406
68
|
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AbstractKeyword)) {
|
|
407
69
|
return violations;
|
|
408
70
|
}
|
|
409
|
-
// Filter to array parameters without validation
|
|
410
71
|
const unvalidatedArrayParams = node.parameters.filter((param) => {
|
|
411
72
|
if (!param.type)
|
|
412
73
|
return false;
|
|
413
|
-
// Primary gate: use AST to determine if the parameter's type is a top-level
|
|
414
|
-
// array type. Object/interface/intersection types whose *properties* are
|
|
415
|
-
// arrays will correctly return false here, eliminating the largest category
|
|
416
|
-
// of false positives.
|
|
417
74
|
if (!isTopLevelArrayType(param.type))
|
|
418
75
|
return false;
|
|
419
76
|
const typeText = param.type.getText(sourceFile);
|
|
420
|
-
// Secondary gate: skip complex/nested types where validation detection is
|
|
421
|
-
// unreliable (Map values, Record values, function types, Promises, etc.)
|
|
422
|
-
/* v8 ignore next -- defensive AST/type guard */
|
|
423
77
|
if (isComplexNestedType(typeText))
|
|
424
78
|
return false;
|
|
425
79
|
return !checkForArrayValidation(node, param, sourceFile);
|
|
@@ -441,25 +95,15 @@ function checkFunctionArrayParams(options) {
|
|
|
441
95
|
}
|
|
442
96
|
return violations;
|
|
443
97
|
}
|
|
444
|
-
/**
|
|
445
|
-
* Analyze a file for array validation issues
|
|
446
|
-
* @param content - The file content as a string
|
|
447
|
-
* @param absolutePath - The absolute path to the file being analyzed
|
|
448
|
-
* @returns Array of violations found in the file
|
|
449
|
-
*/
|
|
450
98
|
function analyzeFile(content, absolutePath) {
|
|
451
99
|
const violations = [];
|
|
452
|
-
// Quick filter: skip files without array-related patterns
|
|
453
100
|
if (!QUICK_FILTER_KEYWORDS.some((kw) => content.includes(kw))) {
|
|
454
101
|
return violations;
|
|
455
102
|
}
|
|
456
|
-
// Note: Ignore directives are handled at the framework level in defineCheck()
|
|
457
103
|
const sourceFile = getSharedSourceFile(absolutePath, content);
|
|
458
|
-
/* v8 ignore next -- defensive guard */
|
|
459
104
|
if (!sourceFile)
|
|
460
105
|
return [];
|
|
461
106
|
const visit = (node) => {
|
|
462
|
-
// Check function parameters with array types
|
|
463
107
|
if (ts.isFunctionDeclaration(node) ||
|
|
464
108
|
ts.isMethodDeclaration(node) ||
|
|
465
109
|
ts.isArrowFunction(node)) {
|
|
@@ -475,7 +119,6 @@ function analyzeFile(content, absolutePath) {
|
|
|
475
119
|
*
|
|
476
120
|
* Detects array parameters without proper validation to prevent
|
|
477
121
|
* runtime errors from unvalidated array inputs.
|
|
478
|
-
*
|
|
479
122
|
*/
|
|
480
123
|
export const arrayValidation = defineCheck({
|
|
481
124
|
id: 'a9e0e70c-a4af-42e6-bbd7-4c87a72cb7d4',
|
|
@@ -499,7 +142,6 @@ export const arrayValidation = defineCheck({
|
|
|
499
142
|
tags: ['quality', 'validation', 'type-safety', 'arrays'],
|
|
500
143
|
fileTypes: ['ts'],
|
|
501
144
|
analyze(content, filePath) {
|
|
502
|
-
// Skip test files — array parameter validation in tests is low-risk due to controlled inputs
|
|
503
145
|
if (isTestFile(filePath))
|
|
504
146
|
return [];
|
|
505
147
|
return analyzeFile(content, filePath);
|