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