Files
BIPMaterialManager/src/main/dao/materials-to-delete.dao.ts
Misaka a7da49ee6f Task 12: Create MaterialsToDeleteDAO with SQL injection protection
Implement MaterialsToDeleteDAO class with secure parameterized queries following
the pattern established in Task 11 (commit 5aafb23).

Features:
- getMaterialsToDeleteByManagers(): Query materials_to_delete table with
  optional filtering by manager_names using parameterized queries
- getAllMaterialsToDelete(): Wrapper to retrieve all materials without filtering

Security:
- Uses parameterized queries (@param0, @param1, etc.) to prevent SQL injection
- Parameters passed separately from query string via executeSQLServerQuery()
- Input validation for empty arrays
- Comprehensive test coverage including SQL injection attempt scenarios

Testing:
- 11 comprehensive unit tests covering all methods and edge cases
- Tests verify parameterized query pattern prevents SQL injection
- All tests passing (12/12 including existing tests)

Files:
- src/main/dao/materials-to-delete.dao.ts: DAO implementation
- tests/unit/dao/materials-to-delete.dao.test.ts: Unit tests

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-28 23:04:43 +08:00

62 lines
2.1 KiB
TypeScript

import { DatabaseService } from '../services/database.service';
import { LoggerService } from '../services/logger.service';
export class MaterialsToDeleteDAO {
constructor(private dbService: DatabaseService) {}
/**
* Query materials_to_delete table and return distinct material codes
* @param managerNames - Optional array of manager names to filter by. Pass null to get all materials.
* @returns Promise<string[]> - Array of distinct material codes
*/
async getMaterialsToDeleteByManagers(
managerNames: string[] | null
): Promise<string[]> {
try {
// Input validation
if (managerNames && managerNames.length === 0) {
LoggerService.warn('Empty manager names array provided, returning empty result');
return [];
}
LoggerService.info(
`Querying materials_to_delete${managerNames ? ` for ${managerNames.length} managers` : ' (all managers)'}`
);
// Build base query
let query = `
SELECT DISTINCT material_code
FROM materials_to_delete
`;
let params: string[] | undefined;
// Add WHERE clause with parameterized query if manager names provided
// SECURITY: Use parameterized queries to prevent SQL injection
if (managerNames && managerNames.length > 0) {
const placeholders = managerNames.map((_, i) => `@param${i}`).join(',');
query += ` WHERE manager_name IN (${placeholders})`;
params = managerNames;
}
// Execute query with parameters (if any)
const result = await this.dbService.executeSQLServerQuery(query, params);
const materials = result.rows.map((row: any) => row.material_code);
LoggerService.info(`Found ${materials.length} materials to delete`);
return materials;
} catch (error) {
LoggerService.error('Failed to query materials_to_delete table', error);
throw error;
}
}
/**
* Get all materials from materials_to_delete table without filtering
* @returns Promise<string[]> - Array of all distinct material codes
*/
async getAllMaterialsToDelete(): Promise<string[]> {
return this.getMaterialsToDeleteByManagers(null);
}
}