fix: replace 'as any' with proper types to resolve linting violations

- Import Select from node-sql-parser for type safety
- Replace function parameter types with Record<string, unknown> for dynamic AST objects
- Use proper type conversions through 'unknown' for incompatible types
- Maintain existing wildcard validation functionality
- Resolve all 8 noExplicitAny linting violations

Co-Authored-By: Dallin Bentley <dallinbentley98@gmail.com>
This commit is contained in:
Devin AI 2025-07-23 13:58:11 +00:00
parent b38940939b
commit 3ac9d8b159
1 changed files with 73 additions and 43 deletions

View File

@ -1,4 +1,4 @@
import { Parser } from 'node-sql-parser';
import { BaseFrom, ColumnRefItem, Join, Parser, type Select } from 'node-sql-parser';
import * as yaml from 'yaml';
export interface ParsedTable {
@ -348,7 +348,10 @@ export function extractTablesFromYml(ymlContent: string): ParsedTable[] {
* Validates that wildcards (SELECT *) are not used on physical tables
* Allows wildcards on CTEs but blocks them on physical database tables
*/
export function validateWildcardUsage(sql: string, dataSourceSyntax?: string): WildcardValidationResult {
export function validateWildcardUsage(
sql: string,
dataSourceSyntax?: string
): WildcardValidationResult {
const dialect = getParserDialect(dataSourceSyntax);
const parser = new Parser();
@ -373,18 +376,18 @@ export function validateWildcardUsage(sql: string, dataSourceSyntax?: string): W
const tableList = parser.tableList(sql, { database: dialect });
const tableAliasMap = new Map<string, string>(); // alias -> table name
if (Array.isArray(tableList)) {
for (const tableRef of tableList) {
if (typeof tableRef === 'string') {
// Simple table name
tableAliasMap.set(tableRef.toLowerCase(), tableRef);
} else if (tableRef && typeof tableRef === 'object') {
const tableRefObj = tableRef as any; // Type assertion to handle dynamic properties
const tableRefObj = tableRef as Record<string, unknown>;
const tableName = tableRefObj.table || tableRefObj.name;
const alias = tableRefObj.as || tableRefObj.alias;
if (tableName) {
if (alias) {
if (tableName && typeof tableName === 'string') {
if (alias && typeof alias === 'string') {
tableAliasMap.set(alias.toLowerCase(), tableName);
}
tableAliasMap.set(tableName.toLowerCase(), tableName);
@ -395,10 +398,13 @@ export function validateWildcardUsage(sql: string, dataSourceSyntax?: string): W
// Check each statement for wildcard usage
const blockedTables: string[] = [];
for (const statement of statements) {
if ('type' in statement && statement.type === 'select') {
const wildcardTables = findWildcardUsageOnPhysicalTables(statement, cteNames);
const wildcardTables = findWildcardUsageOnPhysicalTables(
statement as unknown as Record<string, unknown>,
cteNames
);
blockedTables.push(...wildcardTables);
}
}
@ -424,40 +430,48 @@ export function validateWildcardUsage(sql: string, dataSourceSyntax?: string): W
/**
* Recursively finds wildcard usage on physical tables in a SELECT statement
*/
function findWildcardUsageOnPhysicalTables(selectStatement: any, cteNames: Set<string>): string[] {
function findWildcardUsageOnPhysicalTables(
selectStatement: Record<string, unknown>,
cteNames: Set<string>
): string[] {
const blockedTables: string[] = [];
// Build alias mapping for this statement
const aliasToTableMap = new Map<string, string>();
if (selectStatement.from && Array.isArray(selectStatement.from)) {
for (const fromItem of selectStatement.from) {
if (fromItem.table && fromItem.as) {
const fromItemAny = fromItem as unknown as Record<string, unknown>;
if (fromItemAny.table && fromItemAny.as) {
let tableName: string;
if (typeof fromItem.table === 'string') {
tableName = fromItem.table;
} else if (fromItem.table && typeof fromItem.table === 'object') {
const tableObj = fromItem.table as any;
tableName = tableObj.table || tableObj.name || tableObj.value || String(fromItem.table);
if (typeof fromItemAny.table === 'string') {
tableName = fromItemAny.table;
} else if (fromItemAny.table && typeof fromItemAny.table === 'object') {
const tableObj = fromItemAny.table as Record<string, unknown>;
tableName = String(
tableObj.table || tableObj.name || tableObj.value || fromItemAny.table
);
} else {
continue;
}
aliasToTableMap.set(fromItem.as.toLowerCase(), tableName.toLowerCase());
aliasToTableMap.set(String(fromItemAny.as).toLowerCase(), tableName.toLowerCase());
}
// Handle JOINs
if (fromItem.join && Array.isArray(fromItem.join)) {
for (const joinItem of fromItem.join) {
if (fromItemAny.join && Array.isArray(fromItemAny.join)) {
for (const joinItem of fromItemAny.join) {
if (joinItem.table && joinItem.as) {
let tableName: string;
if (typeof joinItem.table === 'string') {
tableName = joinItem.table;
} else if (joinItem.table && typeof joinItem.table === 'object') {
const tableObj = joinItem.table as any;
tableName = tableObj.table || tableObj.name || tableObj.value || String(joinItem.table);
const tableObj = joinItem.table as Record<string, unknown>;
tableName = String(
tableObj.table || tableObj.name || tableObj.value || joinItem.table
);
} else {
continue;
}
aliasToTableMap.set(joinItem.as.toLowerCase(), tableName.toLowerCase());
aliasToTableMap.set(String(joinItem.as).toLowerCase(), tableName.toLowerCase());
}
}
}
@ -470,7 +484,10 @@ function findWildcardUsageOnPhysicalTables(selectStatement: any, cteNames: Set<s
// Check for unqualified wildcard (SELECT *)
if (column.expr.column === '*' && !column.expr.table) {
// Get all tables in FROM clause that are not CTEs
const physicalTables = getPhysicalTablesFromFrom(selectStatement.from, cteNames);
const physicalTables = getPhysicalTablesFromFrom(
selectStatement.from as unknown as Record<string, unknown>[],
cteNames
);
blockedTables.push(...physicalTables);
}
// Check for qualified wildcard (SELECT table.*)
@ -481,17 +498,19 @@ function findWildcardUsageOnPhysicalTables(selectStatement: any, cteNames: Set<s
tableName = column.expr.table;
} else if (column.expr.table && typeof column.expr.table === 'object') {
// Handle object format - could have table property or be the table name itself
const tableRefObj = column.expr.table as any;
tableName = tableRefObj.table || tableRefObj.name || tableRefObj.value || String(column.expr.table);
const tableRefObj = column.expr.table as Record<string, unknown>;
tableName = String(
tableRefObj.table || tableRefObj.name || tableRefObj.value || column.expr.table
);
} else {
continue; // Skip if we can't determine table name
}
// Check if this is an alias that maps to a CTE
const actualTableName = aliasToTableMap.get(tableName.toLowerCase());
const isAliasToCte = actualTableName && cteNames.has(actualTableName);
const isDirectCte = cteNames.has(tableName.toLowerCase());
if (!isAliasToCte && !isDirectCte) {
blockedTables.push(tableName);
}
@ -503,18 +522,26 @@ function findWildcardUsageOnPhysicalTables(selectStatement: any, cteNames: Set<s
// Check CTEs for nested wildcard usage
if (selectStatement.with && Array.isArray(selectStatement.with)) {
for (const cte of selectStatement.with) {
if (cte.stmt && cte.stmt.type === 'select') {
const subBlocked = findWildcardUsageOnPhysicalTables(cte.stmt, cteNames);
blockedTables.push(...subBlocked);
const cteAny = cte as unknown as Record<string, unknown>;
if (cteAny.stmt && typeof cteAny.stmt === 'object' && cteAny.stmt !== null) {
const stmt = cteAny.stmt as Record<string, unknown>;
if (stmt.type === 'select') {
const subBlocked = findWildcardUsageOnPhysicalTables(stmt, cteNames);
blockedTables.push(...subBlocked);
}
}
}
}
if (selectStatement.from && Array.isArray(selectStatement.from)) {
for (const fromItem of selectStatement.from) {
if (fromItem.expr && fromItem.expr.type === 'select') {
const subBlocked = findWildcardUsageOnPhysicalTables(fromItem.expr, cteNames);
blockedTables.push(...subBlocked);
const fromItemAny = fromItem as unknown as Record<string, unknown>;
if (fromItemAny.expr && typeof fromItemAny.expr === 'object' && fromItemAny.expr !== null) {
const expr = fromItemAny.expr as Record<string, unknown>;
if (expr.type === 'select') {
const subBlocked = findWildcardUsageOnPhysicalTables(expr, cteNames);
blockedTables.push(...subBlocked);
}
}
}
}
@ -525,7 +552,10 @@ function findWildcardUsageOnPhysicalTables(selectStatement: any, cteNames: Set<s
/**
* Extracts physical table names from FROM clause, excluding CTEs
*/
function getPhysicalTablesFromFrom(fromClause: any[], cteNames: Set<string>): string[] {
function getPhysicalTablesFromFrom(
fromClause: Record<string, unknown>[],
cteNames: Set<string>
): string[] {
const tables: string[] = [];
if (!fromClause || !Array.isArray(fromClause)) {
@ -539,18 +569,18 @@ function getPhysicalTablesFromFrom(fromClause: any[], cteNames: Set<string>): st
if (typeof fromItem.table === 'string') {
tableName = fromItem.table;
} else if (fromItem.table && typeof fromItem.table === 'object') {
const tableObj = fromItem.table as any;
tableName = tableObj.table || tableObj.name || tableObj.value || String(fromItem.table);
const tableObj = fromItem.table as Record<string, unknown>;
tableName = String(tableObj.table || tableObj.name || tableObj.value || fromItem.table);
} else {
continue;
}
if (tableName && !cteNames.has(tableName.toLowerCase())) {
const aliasName = fromItem.as || tableName;
tables.push(aliasName);
tables.push(String(aliasName));
}
}
// Handle JOINs
if (fromItem.join && Array.isArray(fromItem.join)) {
for (const joinItem of fromItem.join) {
@ -559,15 +589,15 @@ function getPhysicalTablesFromFrom(fromClause: any[], cteNames: Set<string>): st
if (typeof joinItem.table === 'string') {
tableName = joinItem.table;
} else if (joinItem.table && typeof joinItem.table === 'object') {
const tableObj = joinItem.table as any;
tableName = tableObj.table || tableObj.name || tableObj.value || String(joinItem.table);
const tableObj = joinItem.table as Record<string, unknown>;
tableName = String(tableObj.table || tableObj.name || tableObj.value || joinItem.table);
} else {
continue;
}
if (tableName && !cteNames.has(tableName.toLowerCase())) {
const aliasName = joinItem.as || tableName;
tables.push(aliasName);
tables.push(String(aliasName));
}
}
}