mirror of https://github.com/buster-so/buster.git
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:
parent
b38940939b
commit
3ac9d8b159
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue